Spring中ApplicationListener -【Spring底层原理】

本文详细介绍了Spring框架中如何通过监听器实现容器启动、关闭事件的捕获,以及自定义事件的发布和处理过程。通过源码分析,揭示了事件从发布到监听的完整流程,包括事件的派发机制和容器中监听器的注册。
摘要由CSDN通过智能技术生成
  1. ContextStartedEvent:容器开始执行时发布这个事件

实现步骤:

  1. 写一个监听器(ApplicationListener 实现类)来监听某个事件(ApplicationEvent及其子类)

  2. 把监听器加到容器中

  3. 只要容器中有相关事件的发布,我们就能监听到这个事件,监听的便是上面说到的子事件

  4. 自定义发布一个事件:applicationContext.publishEvent()

二、实例分析

// 启动测试类

@Test

public void TestMain(){

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

// 自己发布一个事件

applicationContext.publishEvent(new ApplicationEvent(“自己发布的事件”) {

});

applicationContext.close();

}

// ApplicationListener实现类

@Component

public class MyApplicationListener implements ApplicationListener {

// 当容器中发布此事件后,该方法会触发

public void onApplicationEvent(ApplicationEvent applicationEvent) {

System.out.println(“收到的事件:” + applicationEvent);

}

}

// 配置类

@Configuration

@ComponentScan(“listener”)

public class AppConfig {

}

运行启动类,输出结果如下,以下三点说一下:

  • 容器启动时,会执行容器刷新完成事件,也就是ContextRefreshedEvent

  • 容器关闭时,会执行容器关闭事件,也就是ContextClosedEvent

  • 在启动类中,通过publishEvent来发布事件,执行这个方法的时候,ApplicationListener 就能监听到这个事件,就会回调onApplicationEvent执行

image-20210320174351733

三、源码分析

在上面的案例中,收到了三个事件,分别是:ContextRefreshedEventContextClosedEvent以及自己定义的MainTest$1[source=自己发布的事件],这几个事件在底层是如何收到的呢?,我们就通过源码来进行分析,在回掉方法onApplicationEvent打上断点,通过Debug查看源码:

【1】事件发布

通过Debug,我们可以看到,最先收到ContextRefreshedEvent事件,下面咱们就根据方法调用栈分析ContextRefreshedEvent如何发布的

image-20210320201912672

容器创建对象,调用refresh()方法——>finishRefresh()方法——>publishEvent()方法,调用getApplicationEventMulticaster()方法获取事件的多播器,也就是派发器

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {

Assert.notNull(event, “Event must not be null”);

Object applicationEvent;

if (event instanceof ApplicationEvent) {

applicationEvent = (ApplicationEvent)event;

} else {

applicationEvent = new PayloadApplicationEvent(this, event);

if (eventType == null) {

eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();

}

}

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);

}

}

}

调用multicastEvent进行事件的派发

  • 获取所有ApplicationListener进行遍历

  • 判断是否可以用executor异步执行,可以的话使用executor进行异步派发,派发的时候我们可以自定义是同步还是异步

  • 否则同步执行派发

public void multicastEvent(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进行遍历

ApplicationListener<?> listener = (ApplicationListener)var5.next();

// 判断是否可以用executor异步执行,可以的话使用executor进行异步派发

if (executor != null) {

executor.execute(() -> {

this.invokeListener(listener, event);

});

} else {

// 否则同步执行

this.invokeListener(listener, event);

}

}

}

执行invokeListener方法,拿到listener回调onApplicationEvent方法

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {

try {

// 执行invokeListener方法,拿到listener回调onApplicationEvent方法

listener.onApplicationEvent(event);

} catch (ClassCastException var6) {

String msg = var6.getMessage();

if (msg != null && !this.matchesClassCastMessage(msg, event.getClass())) {

throw var6;

}

Log logger = LogFactory.getLog(this.getClass());

if (logger.isTraceEnabled()) {

logger.trace("Non-matching event type for listener: " + listener, var6);

}

}

}

【2】获取事件派发器getApplicationEventMulticaster

容器创建对象,调用refresh()方法——>initApplicationEventMulticaster()方法,初始化ApplicationEventMulticaster

  • 先从容器中找是否有ID为“applicationEventMulticaster”的组件

  • 有,则通过getBean的方式获取到该组件

  • 如果没有,则创建一个简单的ApplicationEventMulticaster

  • 将创建的注册到容器的单实例bean中,这样我们就可以在其他组件要派发事件,自动注入这个applicationEventMulticaster

protected void initApplicationEventMulticaster() {

ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();

// 从bean工厂中找是否有ID为“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 {

// 如果没有则自己创建一个简单的ApplicationEventMulticaster

this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);

// 将创建的注册到容器的单实例bean中

beanFactory.registerSingleton(“applicationEventMulticaster”, this.applicationEventMulticaster);

if (this.logger.isTraceEnabled()) {

this.logger.trace(“No ‘applicationEventMulticaster’ bean, using [” + this.applicationEventMulticaster.getClass().getSimpleName() + “]”);

}

}

}

【3】容器中有哪些监听器

容器创建对象,调用refresh()方法——>registerListeners()方法,注册监听器

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

对于很多Java工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。

整理的这些资料希望对Java开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

再分享一波我的Java面试真题+视频学习详解+技能进阶书籍

美团二面惜败,我的凉经复盘(附学习笔记+面试整理+进阶书籍)

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
36.jpg" alt=“img” style=“zoom: 33%;” />

最后

对于很多Java工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。

整理的这些资料希望对Java开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

再分享一波我的Java面试真题+视频学习详解+技能进阶书籍

[外链图片转存中…(img-jIwtMRhn-1713333447468)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值