SpringBoot ApplicationListener原理解析

前言

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

因为是调用SpringApplicationaddListeners()方法进行注册器监听的,所以首当其冲地查看该方法实现,发现注册监听器不过是往SpringApplicationlisteners(监听器列表)中添加监听器而已。

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()方法

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

那么我们可以得出清晰的调用链路或者说关联关系

SpringApplication ------> SpringApplicationRunListeners ------> SpringApplicationRunListener | EventPublishingRunListener ------> SimpleApplicationEventMulticaster ------> ApplicationListener

事件分发

因为自定义的ReadyListener关注的是ApplicationReadyEvent事件,那么直接从SpringApplicationRunListenersready()方法开始切入。

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

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

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

multicastEvent()

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

获取ApplicationListener

getApplicationListeners()

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

缓存ApplicationListener

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

retrieveApplicationListeners()

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

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

CachedListenerRetriever

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

总结

通过ApplicationListener实现事件监听,虽然涉及到了很多类,各种类之间的关联关系有点复杂,但是整体思路还是简单的,也是23种设计模式之一《观察者模式》的体现。

本质就是从Spring中获取所有ApplicationListener,并根据这些listener所关注的ApplicationEvent的不同,给listener做一个分类并缓存,在调用不同方法进行事件分发时,获取相关的listener集合,逐个遍历并调用其onApplicationEvent()方法。

结尾

本文章源自《Learn SpringBoot》专栏,感兴趣的话还请关注点赞收藏.

上一篇文章:《SpringBoot ApplicationContextInitializer系统初始化器原理解析

相关推荐
IT学长编程1 小时前
计算机毕业设计 玩具租赁系统的设计与实现 Java实战项目 附源码+文档+视频讲解
java·spring boot·毕业设计·课程设计·毕业论文·计算机毕业设计选题·玩具租赁系统
莹雨潇潇1 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
杨哥带你写代码1 小时前
足球青训俱乐部管理:Spring Boot技术驱动
java·spring boot·后端
郭二哈2 小时前
C++——模板进阶、继承
java·服务器·c++
A尘埃2 小时前
SpringBoot的数据访问
java·spring boot·后端
yang-23072 小时前
端口冲突的解决方案以及SpringBoot自动检测可用端口demo
java·spring boot·后端
沉登c2 小时前
幂等性接口实现
java·rpc
代码之光_19802 小时前
SpringBoot校园资料分享平台:设计与实现
java·spring boot·后端
科技资讯早知道3 小时前
java计算机毕设课设—坦克大战游戏
java·开发语言·游戏·毕业设计·课程设计·毕设
苹果醋34 小时前
快速玩转 Mixtral 8x7B MOE大模型!阿里云机器学习 PAI 推出最佳实践
spring boot·nginx·毕业设计·layui·课程设计