IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> spring源码解读系列(八):观察者模式--spring监听器详解 -> 正文阅读

[Java知识库]spring源码解读系列(八):观察者模式--spring监听器详解

一、前言

在前面的文章spring源码解读系列(八)中,我们继续剖析了spring的核心refresh()方法中的registerBeanPostProcessors(beanFactory)(完成BeanPostProcessor的注册)和initMessageSource()(为上下文初始化message源,即不同语言的消息体,国际化处理),有心的朋友顺着我们的思路继续看,发现下面开始了initApplicationEventMulticaster()(初始化事件监听多路广播器),那么什么又是多路广播器呢?这和spring中的监听器什么关系?这个和观察者模式又是什么关系?本文将带你一探究竟,理顺整个脉络。

二、观察者模式的定义

观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式:
观察者模式是一种对象行为型模式,其主要优点如下。
? ? ? ? ? 1.降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
? ? ? ? ? 2.目标与观察者之间建立了一套触发机制
主要缺点如下:
? ? ? ? ? 1.目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
? ? ? ? ? 2.当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。

三、jdk中的观察者模式

请添加图片描述
原理解析:
1.用户自定义类继承Observable,作为被观察者,自定义方法执行需要的业务逻辑,执行完成后,通过Observable中的notifyObservers()方法通知观察者
2.用户自定义类实现Observer接口,重写update方法,处理被观察者发生行为后触发的逻辑。
3.调用Observable中的addObserver方法,添加对应的观察者

自己实现一个简单的案例:

package edu.jiahui.weixingateway.utils;

import java.util.Observable;
import java.util.Observer;

/**
 * 定义被观察者
 */
public class ExpertObservable extends Observable {

    public void writeBlog(){
        System.out.println("大佬在写博客");
        // 设置changed = true,表示发生了变化
        super.setChanged();
        super.notifyObservers();
    }

}

/**
 * 定义观察者-小明
 */
class XiaoMing implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("小明在点赞!");
    }
}
/**
 * 定义观察者-小红
 */
class XiaoHong implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("小红在评论!");
    }
}

/**
 * 测试类
 */
class TestObserver{

    public static void main(String[] args) {
        // 创建被观察者对象
        final ExpertObservable expertObservable = new ExpertObservable();
        // 添加观察者
        expertObservable.addObserver(new XiaoMing());
        expertObservable.addObserver(new XiaoHong());
        // 被观察者执行方法
        expertObservable.writeBlog();
    }
}

输出结果
请添加图片描述

四、spring中的观察者模式

spring中的事件监听机制,很多人也叫做监听器模式,其实是标准的观察者模式(23中设计模式中并没有监听器模式),只不过对观察者模式实现了更加的抽象化和细化,其中主要有四个角色:
ApplicationListener:事件监听器,等同于jdk中的观察者Observer,实现类继承该接口,重写onApplicationEvent方法,处理对事件发生的响应。
ApplicationEventMulticaster:事件多路广播器,等同于jdk中被观察者Observable中的obs属性,负责添加、移除、通知观察者,可以看下它的类结构,是不是很眼熟
请添加图片描述
ApplicationEvent:事件,监听器监听到事件后作出响应,类似于我们上面例子中的writeBlog()方法就是一个事件,只不过spring这里进行了抽象,抽象出了这个具体的接口,运用起来更加灵活,扩展性更强,在此进一步感叹下,源码的牛逼!
ApplicationEventPublisher:事件发布者,即事件源,产生事件的主体,等同于jdk中的被观察者Observable,我们研究spring源码中的AbstractApplicationContext实现了该接口,即是一个事件源,通过调用publishEvent方法发布事件。
Spring事件监听机制流程:
请添加图片描述
流程解释
1.流程分为两个阶段
? ? ? ? ? (1)一个是启动Spring容器
? ? ? ? ? (2)另外一个是我们触发事件的时候
2.核心还是事件广播器ApplicationEventMulticaster(这里实际指的是它的实现类ApplicationEventMulticaster,SimpleApplicationEventMulticaster)
3.增加监听器是在启动Spring容器时候完成的(图中紫红色的部分)。这也是Spring容器的核心位置。为防止读者在自己看源码的时候疑惑,图中我特意把两个加载linstener的过程都画出来。这两个addxxx分别是:
? ? ? ? ? (1)增加普通的监听器,即通过实现ApplicationListener实现的监听器
? ? ? ? ? (2)增加使用注解(@EventListener)实现的监听器
4.事件发布。这是我们写程序可触及到的一部分流程。核心是ApplicationEventPublisher。这里会首先去调用事件广播器的getApplicationListeners方法,拿到所有的监听器(由于前面启动时已经加载里所有监听器,所以这里可以拿到),然后逐个调用监听器内的方法。

五、initApplicationEventMulticaster()

掌握了上面的基本知识,我们就可以很轻松惬意的继续解读spring的源码了,顺着refresh()方法,继续解读initApplicationEventMulticaster()方法:

	protected void initApplicationEventMulticaster() {
		// 获取当前bean工厂,一般是DefaultListableBeanFactory
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		// 判断容器中是否存在bdName为applicationEventMulticaster的bd,也就是说自定义的事件监听多路广播器,必须实现ApplicationEventMulticaster接口
		if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
			// 如果有,则从bean工厂得到这个bean对象
			this.applicationEventMulticaster =
					beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
			if (logger.isTraceEnabled()) {
				logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
			}
		}
		else {
			// 如果没有,则默认采用SimpleApplicationEventMulticaster
			this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
			beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
			if (logger.isTraceEnabled()) {
				logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
						"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
			}
		}
	}

这里即是初始化一个多路广播器,为后续的发布事件作准备,原理也很简单:spring判断beanFactory中是否含有用户自定义的applicationEventMulticaster,如果有,就使用用户自定义的,否则,则使用默认的SimpleApplicationEventMulticaster。

六、onRefresh()

这个方法在spring中是个空实现,主要留给子类扩展,这里不做过多解读,在springboot中有具体的实现,用以完成创建web容器,详见org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh方法
请添加图片描述
这里完成了web容器的创建,springboot默认tomcat,感兴趣的朋友可以研究下,本人也会在后续博客中推出springboot源码解析系列,作义详细探讨。

七、registerListeners()

见名知义,注册监听器,即将目前可以获取到的所有监听器ApplicationListener,注册到多路广播器ApplicationEventMulticaster中,详见源码解析:

	protected void registerListeners() {
		// Register statically specified listeners first.
		// 遍历应用程序中存在的监听器集合,并将对应的监听器添加到监听器的多路广播器中
		for (ApplicationListener<?> listener : getApplicationListeners()) {
			getApplicationEventMulticaster().addApplicationListener(listener);
		}

		// Do not initialize FactoryBeans here: We need to leave all regular beans
		// uninitialized to let post-processors apply to them!
		// 从容器中获取所有实现了ApplicationListener接口的bd的bdName
		// 放入ApplicationListenerBeans集合中
		String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
		for (String listenerBeanName : listenerBeanNames) {
			getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
		}

		// Publish early application events now that we finally have a multicaster...
		// 此处先发布早期的监听器集合
		Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
		this.earlyApplicationEvents = null;
		if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
			for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
				getApplicationEventMulticaster().multicastEvent(earlyEvent);
			}
		}
	}

总结为三步:
1.从AbstractApplicationContext类中的Set<ApplicationListener<?>> applicationListeners取出用户添加的监听器ApplicationListener,添加到多路广播器中
2.从spring工厂中取出符合条件的ApplicationListener,添加到多路广播器中
3.对早期添加在earlyApplicationEvents中的事件进行发布

通过debug发现,spring这里的监听器和事件earlyApplicationEvents都为空,主要是为了扩展方便,在springmvc和springboot中有具体的监听器和事件,感兴趣的可以自己研究下,我也会在后续的springmvc和springboot源码解析中带着大家学习。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-18 12:34:18  更:2021-08-18 12:35:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/21 5:43:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码