开启生长之旅!这是我参与「日新计划 12 月更文挑战」的第19天,点击查看活动详情
前语
这几天在研究Spring
的生命周期,忽然被女朋友问了一个问题,Spring
的事情监听与发布是怎么完结的?我想了想说,可能是经过中心存储,ApplicationEventPublisher
把事情发布到中心存储,监听器只要监听中心存储就能完结这个功用吧。今日看了一下源码,结果并不是!
初始化事情多播器与监听器注册
在Spring
初始化过程中,咱们可以在refresh()
办法中看到以下代码:
// 初始化事情多播器
this.initApplicationEventMulticaster();
this.onRefresh();
// 注册监听器
this.registerListeners();
咱们来看看这个事情多播器是怎么初始化的:
protectedvoidinitApplicationEventMulticaster() {
ConfigurableListableBeanFactorybeanFactory=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=newSimpleApplicationEventMulticaster(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
目标;
下面咱们看看事情监听器是怎么注册进来的:
protectedvoidregisterListeners() {
// 遍历applicationListeners链表中的事情监听器,由于可能有一部分监听器经过addApplicationListener()办法增加;归于api的办法增加
Iteratorvar1=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;
intvar3=listenerBeanNames.length;
// 顺次把对应的Bean目标增加到多播器中
for(intvar4=0;var4<var3;++var4) {
StringlistenerBeanName=var7[var4];
this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// 把监听器还没注册之前就发布的事情顺次调用multicastEvent()办法发布出来
Set<ApplicationEvent>earlyEventsToProcess=this.earlyApplicationEvents;
this.earlyApplicationEvents=null;
if(!CollectionUtils.isEmpty(earlyEventsToProcess)) {
Iteratorvar9=earlyEventsToProcess.iterator();
while(var9.hasNext()) {
ApplicationEventearlyEvent=(ApplicationEvent)var9.next();
this.getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
上面的源码分红大约三部分:
1.先处理一下经过api办法增加的
ApplicationListener
,比如ApplicationContext.addApplicationListener()
增加的事情监听器都会暂时放在链表中,直至
registerListeners()
办法的履行才会把它们放进多播器中;2.从
IOC
容器中找出一切完结了ApplicationListener
的Bean
目标,把它们放进多播器中;3.把监听器还没注册之前就现已发布的事情经过多播器完结发布出来;
事情发布与监听
当咱们的Spring
启动结束后,也就意味着事情多播器与监听器都准备好了,那么咱们来看看事情发布是怎么进行的,咱们来看一下publishEvent()
办法:
publicvoidpublishEvent(Objectevent) {
// 调用的下面的publishEvent办法
this.publishEvent(event, (ResolvableType)null);
}
protectedvoidpublishEvent(Objectevent,@NullableResolvableTypeeventType) {
Assert.notNull(event,"Event must not be null");
ObjectapplicationEvent;
// 假如发布的event完结了ApplicationEvent,那么就做一下类型转化
if(eventinstanceofApplicationEvent) {
applicationEvent=(ApplicationEvent)event;
}else{
// 假如没有完结ApplicationEvent,那么对它做一层包装,依然还是转化成ApplicationEvent
applicationEvent=newPayloadApplicationEvent(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.parentinstanceofAbstractApplicationContext) {
((AbstractApplicationContext)this.parent).publishEvent(event,eventType);
}else{
this.parent.publishEvent(event);
}
}
}
事情发布的逻辑也可以分红三步:
1.发布的事情需求检查类型是否是
ApplicationEvent
,不是ApplicationEvent
也会被包装成ApplicationEvent
类型,包装事情类型的统一;2.事情发布的时候需求考虑事情监听器是否现已完结了注册,这里边经过判别
earlyApplicationEvents
是否为null
来确认;在没有完结事情监听器注册的时候,一切发布的事情都会被暂存在earlyApplicationEvents
中,事情监听器注册结束,earlyApplicationEvents
把暂存的事情悉数发布结束,earlyApplicationEvents
被置为null
;事情监听器假如现已注册结束,那么直接经过多播器发布事情;3.假如当前容器还有父容器,那么父容器也要可以监听到发布的事情;
publicvoidmulticastEvent(finalApplicationEventevent,@NullableResolvableTypeeventType) {
ResolvableTypetype=eventType!=null?eventType:this.resolveDefaultEventType(event);
// 是否指定处理的线程池
Executorexecutor=this.getTaskExecutor();
Iteratorvar5=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);
}
}
}
在事情发布中,大约分红以下两部处理:
1.假如监听器有指定线程池的话,那么会把处理使命交给线程池;
2.假如没有指定线程池的话,那么直接调用监听器的onApplicationEvent()办法处理;
小结
综上所述,并不是我一开始瞎想的那样,Spring
借助一个多播器经过观察者的规划模式完结了事情的发布与监听功用,里边的一些细节咱们有必要了解一下:
1.事情监听器没有注册前也是可以发布事情的,只不过会延迟到事情监听器刚注册结束再发布;
2.事情监听是可以配置线程池的,这样就可以异步来履行监听器的处理逻辑;