这几天在研究Spring的生命周期,突然被女朋友问了一个问题,Spring的事件监听与发布是怎么实现的?我想了想说,可能是通过中间存储,ApplicationEventPublisher把事件发布到中间存储,监听器只要监听中间存储就能实现这个功能吧。今天看了一下源码,结果并不是!
初始化事件多播器与监听器注册
在Spring初始化过程中,我们可以在refresh()方法中看到以下代码:
// 初始化事件多播器
this.initApplicationEventMulticaster();
this.onRefresh();
// 注册监听器
this.registerListeners();
我们来看看这个事件多播器是如何初始化的:
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
// 如果IOC容器中已经有applicationEventMulticaster这个Bean的话,直接赋值给applicationEventMulticaster
if (beanFactory.containsLocalBean("applicationEventMulticaster")) {
this.applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
} else {
// 如果容器中没有,那么创建一个SimpleApplicationEventMulticaster,并且注册到IOC容器中
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton("applicationEventMulticaster", this.applicationEventMulticaster);
if (this.logger.isTraceEnabled()) {
this.logger.trace("No 'applicationEventMulticaster' bean, using [" +this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}
所以说,默认的事件多播器就是SimpleApplicationEventMulticaster,此时this.applicationEventMulticaster对应的就是SimpleApplicationEventMulticaster对象;
下面我们看看事件监听器是如何注册进来的:
protected void registerListeners() {
// 遍历applicationListeners链表中的事件监听器,因为可能有一部分监听器通过addApplicationListener()方法添加;属于api的方式添加
Iterator var1 = this.getApplicationListeners().iterator();
// 把所有的事件监听器添加到多播器中
while(var1.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var1.next();
this.getApplicationEventMulticaster().addApplicationListener(listener);
}
// 从当前容器中找所有ApplicationListener子类;这一部分属于注解|配置方式添加监听器
String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true, false);
String[] var7 = listenerBeanNames;
int var3 = listenerBeanNames.length;
// 依次把对应的Bean对象添加到多播器中
for(int var4 = 0; var4 < var3; ++var4) {
String listenerBeanName = var7[var4];
this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// 把监听器还没注册之前就发布的事件依次调用multicastEvent()方法发布出来
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
Iterator var9 = earlyEventsToProcess.iterator();
while(var9.hasNext()) {
ApplicationEvent earlyEvent = (ApplicationEvent)var9.next();
this.getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
上面的源码分成大概三部分:
- 先处理一下通过api方式添加的ApplicationListener,比如ApplicationContext.addApplicationListener()
添加的事件监听器都会暂时放在链表中,直至registerListeners()方法的执行才会把它们放进多播器中; - 从IOC容器中找出所有实现了ApplicationListener的Bean对象,把它们放进多播器中;
- 把监听器还没注册之前就已经发布的事件通过多播器实现发布出来;
事件发布与监听
当我们的Spring启动完毕后,也就意味着事件多播器与监听器都准备好了,那么我们来看看事件发布是如何进行的,我们来看一下publishEvent()方法:
public void publishEvent(Object event) {
// 调用的下面的publishEvent方法
this.publishEvent(event, (ResolvableType)null);
}
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
Object applicationEvent;
// 如果发布的event实现了ApplicationEvent,那么就做一下类型转换
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent)event;
} else {
// 如果没有实现ApplicationEvent,那么对它做一层包装,依然还是转换成ApplicationEvent
applicationEvent = new PayloadApplicationEvent(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
}
}
// 如果earlyApplicationEvents不为null,说明此时事件监听器还没完成注册,就先把需要发布的事件暂存在earlyApplicationEvents中
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
} else {
// 这个时候事件监听器已经完成注册,可以正常发布事件;
// 直接调用事件多播器完成事件发布
this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
}
// 如果还有父容器,那么父容器也要收到发布的事件
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
} else {
this.parent.publishEvent(event);
}
}
}
事件发布的逻辑也可以分成三步:
- 发布的事件需要检查类型是否是ApplicationEvent,不是ApplicationEvent也会被包装成ApplicationEvent类型,包装事件类型的统一;
- 事件发布的时候需要考虑事件监听器是否已经完成了注册,这里面通过判断earlyApplicationEvents是否为null来确定;在没有完成事件监听器注册的时候,所有发布的事件都会被暂存在earlyApplicationEvents中,事件监听器注册完毕,earlyApplicationEvents把暂存的事件全部发布完毕,earlyApplicationEvents被置为null;事件监听器如果已经注册完毕,那么直接通过多播器发布事件;
- 如果当前容器还有父容器,那么父容器也要能够监听到发布的事件;
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
// 是否指定处理的线程池
Executor executor = this.getTaskExecutor();
Iterator var5 = this.getApplicationListeners(event, type).iterator();
// 遍历监听该事件的所有监听器
while(var5.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var5.next();
// 如果知道的线程池不为null,那么交给线程池异步处理
if (executor != null) {
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
// 直接当前线程处理,最终调用到监听器的onApplicationEvent()方法
this.invokeListener(listener, event);
}
}
}
在事件发布中,大概分成以下两部处理:
- 如果监听器有指定线程池的话,那么会把处理任务交给线程池;
- 如果没有指定线程池的话,那么直接调用监听器的onApplicationEvent()方法处理;
小结
综上所述,并不是我一开始瞎想的那样,Spring借助一个多播器通过观察者的设计模式完成了事件的发布与监听功能,里面的一些细节我们有必要了解一下:
- 事件监听器没有注册前也是可以发布事件的,只不过会延迟到事件监听器刚注册完毕再发布;
- 事件监听是可以配置线程池的,这样就可以异步来执行监听器的处理逻辑;
作者:湘小宝666
链接:https://juejin.cn/post/7177673973621063736
来源:稀土掘金
网友评论