前言
SpringBoot从创建到销毁的完整生命周期过程中,会在不同阶段广播不同的事件,我们若如果想要在某个阶段执行某些逻辑,则需要往SpringBoot中注册监听器,以便SpringBoot在广播事件时触发我们的事件监听回调,具体应用之前有讲过SpringBoot核心特性------应用事件监听 ,本文则是从源码的角度来进行深度解析
示例
新建一个ReadyListener
java
package geek.springboot.application.listener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
/**
* 监听{@link ApplicationReadyEvent}事件
*
* @author Bruse
*/
@Slf4j
public class ReadyListener implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
log.info(" application is ready");
}
}
注册ReadyListener
java
package geek.springboot.application;
import geek.springboot.application.listener.ReadyListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@Slf4j
@SpringBootApplication
public class Application {
public static void main(String[] args) {
// 声明SpringApplication
SpringApplication application = new SpringApplication(Application.class);
// 注册监听器
application.addListeners(new ReadyListener());
// 启动SpringApplication
application.run(args);
}
}
启动输出如下

源码分析
关联关系
SpringApplication.listeners
因为是调用SpringApplication的addListeners()方法进行注册器监听的,所以首当其冲地查看该方法实现,发现注册监听器不过是往SpringApplication的listeners(监听器列表)中添加监听器而已。


有setter就有getter,那么再去查看调用该变量的地方有哪些

EventPublishingRunListener
接着找到了EventPublishingRunListener,从注释可以看出这个类会通过使用内部的ApplicationEventMulticaster进行事件处理。

为了印证猜想,打上断点并启动,可以看到不止是自定义的ReadyListener实例,还有一些ApplicationListener实例也被添加到了initialMulticaster中。

EventPublishingRunListener如何初始化的
接下来查看该构造函数的调用地方,结果却是没有代码引用

换成按类名搜索引用的话,可以看到EventPublishingRunListener其实是在META-INF/spring.factories中进行注册的,它会被SpringFactoriesLoader扫描并进行初始化

关于SpringFactoriesLoader不懂的可以看这里:SpringBoot ApplicationContextInitializer系统初始化器原理解析

SpringApplicationRunListener
与此同时还看到其实EventPublishingRunListener实现了SpringApplicationRunListener接口,由注释可以看到它是一个SpringApplication运行方法的监听器,而且它也是由SpringFactoriesLoader加载的。

SpringApplicationRunListeners
那么查找接口的引用,顺着找到原来是SpringApplicationRunListeners在引用,由注释也可以看出它其实算是包装了SpringApplicationRunListener实现的集合。

SpringApplicaiton.getRunListeners()
再次查找SpringApplicationRunListeners的引用,找到了SpringApplication.getRunListeners()方法

顺藤摸瓜,可以看出在调用SpringApplication的run()方法时,就已经初始化并获取了SpringApplicationRunListeners实现,其包含了所有SpringApplicationRunListener接口实现,也就包含了EventPublishingRunListener,而它内部又是依靠SimpleApplicationEventMulticaster处理所有ApplicationListener事件监听。

那么我们可以得出清晰的调用链路或者说关联关系
SpringApplication ------> SpringApplicationRunListeners ------> SpringApplicationRunListener | EventPublishingRunListener ------> SimpleApplicationEventMulticaster ------> ApplicationListener
事件分发
因为自定义的ReadyListener关注的是ApplicationReadyEvent事件,那么直接从SpringApplicationRunListeners的ready()方法开始切入。


逐个遍历SpringApplicationRunListener并调用其ready()方法

来到EventPublishingRunListener的ready()方法,声明了一个ApplicationReadyEvent事件,并交由ApplicationContext进行事件推送。

但其实一步步往下执行,你会发现最终其实也还是交给SimpleApplicationEventMulticaster的multicastEvent()方法进行处理。

multicastEvent()
可以看到multicastEvent()方法,其实就是获取当前对ApplicationReadyEvent事件感兴趣的ApplicationListener,然后逐个调用其onApplicationEvent()方法。



获取ApplicationListener
getApplicationListeners()
深入一下getApplicationListeners()方法实现,可以看到该方法其实是返回与给定事件类型匹配的ApplicationListener集合,因为在前面调用EventPublishingRunListener的ready()方法时,声明的是ApplicationReadyEvent,所以在这里只会返回关心ApplicationReadyEvent事件的listener。

缓存ApplicationListener
可以看到这个方法内部使用了缓存机制,先查询缓存中是否存在关注该ApplicationEvent的ApplicationListener集合,有的话直接返回,否则再检索出相应的ApplicationListener,并放入到缓存中,下次再调用getApplicationListeners()时,则会直接返回缓存相关的listeners了。 

retrieveApplicationListeners()
根据事件检索出关注该事件的listener是在AbstractApplicationEventMulticaster的retrieveApplicationListeners()方法实现的,有兴趣可以自行研究一下。

以下是获取当前注册的所有ApplicationListener,并根据ApplicationEvent事件类型筛选出仅关注该事件的listener相关代码


CachedListenerRetriever
筛选出来的ApplicationListener则是交由CachedListenerRetriever存放,从注释也可以看出该类的主要作用就是用来缓存特定的listener,有兴趣的话可以再自行研究该类源码。

总结
通过ApplicationListener实现事件监听,虽然涉及到了很多类,各种类之间的关联关系有点复杂,但是整体思路还是简单的,也是23种设计模式之一《观察者模式》的体现。
本质就是从Spring中获取所有ApplicationListener,并根据这些listener所关注的ApplicationEvent的不同,给listener做一个分类并缓存,在调用不同方法进行事件分发时,获取相关的listener集合,逐个遍历并调用其onApplicationEvent()方法。
结尾
本文章源自《Learn SpringBoot》专栏,感兴趣的话还请关注点赞收藏.