前言
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》专栏,感兴趣的话还请关注点赞收藏.