在前面的文章spring源码解读系列(七)中,我们继续剖析了spring的核心refresh()方法中的registerBeanPostProcessors(beanFactory)(完成BeanPostProcessor的注册)和initMessageSource()(为上下文初始化message源,即不同语言的消息体,国际化处理),有心的朋友顺着我们的思路继续看,发现下面开始了initApplicationEventMulticaster()(初始化事件监听多路广播器),那么什么又是多路广播器呢?这和spring中的监听器什么关系?这个和观察者模式又是什么关系?本文将带你一探究竟,理顺整个脉络。
观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式:
观察者模式是一种对象行为型模式,其主要优点如下。
1.降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
2.目标与观察者之间建立了一套触发机制
主要缺点如下:
1.目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
2.当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
原理解析:
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中的事件监听机制,很多人也叫做监听器模式,其实是标准的观察者模式(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方法,拿到所有的监听器(由于前面启动时已经加载里所有监听器,所以这里可以拿到),然后逐个调用监听器内的方法。
掌握了上面的基本知识,我们就可以很轻松惬意的继续解读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。
这个方法在spring中是个空实现,主要留给子类扩展,这里不做过多解读,在springboot中有具体的实现,用以完成创建web容器,详见org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh方法
这里完成了web容器的创建,springboot默认tomcat,感兴趣的朋友可以研究下,本人也会在后续博客中推出springboot源码解析系列,作义详细探讨。
见名知义,注册监听器,即将目前可以获取到的所有监听器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
2.从spring工厂中取出符合条件的ApplicationListener,添加到多路广播器中
3.对早期添加在earlyApplicationEvents中的事件进行发布
通过debug发现,spring这里的监听器和事件earlyApplicationEvents都为空,主要是为了扩展方便,在springmvc和springboot中有具体的监听器和事件,感兴趣的可以自己研究下,我也会在后续的springmvc和springboot源码解析中带着大家学习。