Spring监听器之ApplicationListener原理及源码深度解析
一、原理及源码解析
在Spring框架中,事件驱动模型是一个核心但常被忽视的机制。它主要围绕几个关键事件展开:ContextRefreshedEvent、开发者自定义发布的事件(如示例中的IOCTest_Ext$1[source=我发布的事件]),以及ContextClosedEvent。
免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈
那么,这些事件是如何产生并被处理的呢?我们不妨深入到源码层面,一探究竟。
事件的生命周期与触发时机
- ContextRefreshedEvent事件:它的诞生与容器的生命周期紧密相连。
- 容器在创建对象时,会调用核心的
refresh()方法。 - 当
refresh()流程执行完毕,即容器刷新完成时,会通过finishRefresh()方法发布ContextRefreshedEvent事件。
- 容器在创建对象时,会调用核心的
- 自定义发布事件:开发者可以在任何业务逻辑中,通过
applicationContext.publishEvent()主动发布事件。 - ContextClosedEvent事件:当容器关闭时,会自动发布此事件。
【事件发布流程】源码执行流程
当调用publishEvent(new ContextRefreshedEvent(this));时,幕后发生了什么?
- 获取事件派发器:首先,Spring会通过
getApplicationEventMulticaster()获取事件的多播器(或称派发器)。 - 派发事件:接着,调用
multicastEvent来派发事件。 - 获取监听器:派发器会获取所有与当前事件匹配的
ApplicationListener。这个过程大致如下:for (final ApplicationListener> listener : getApplicationListeners(event, type)) {- 异步派发支持:如果配置了
Executor,Spring会使用它进行异步派发,提升性能。 - 同步执行:如果没有配置
Executor,则采用同步方式,直接调用invokeListener(listener, event);。最终,会回调监听器的onApplicationEvent方法。
- 异步派发支持:如果配置了
【事件多播器(派发器)】源码执行流程
这个关键的派发器又是如何初始化的呢?
- 同样始于容器的
refresh()方法。 - 在
initApplicationEventMulticaster()方法中,Spring会初始化ApplicationEventMulticaster。- 它会先去容器中查找是否存在
id="applicationEventMulticaster"的组件。 - 如果找不到,Spring会创建一个默认的
SimpleApplicationEventMulticaster(beanFactory)实例,并将其注册到容器中。这样一来,其他需要派发事件的组件就能自动注入这个多播器了。
- 它会先去容器中查找是否存在
【容器中有哪些监听器】源码执行流程
监听器又是如何被收集并注册到派发器中的?
- 依旧是
refresh()方法。 - 在
registerListeners()阶段,Spring会从容中找出所有类型为ApplicationListener的Bean。String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); //将listener注册到ApplicationEventMulticaster中 getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
SmartInitializingSingleton 原理
除了事件监听,还有一个与之相关的接口SmartInitializingSingleton,其核心方法是afterSingletonsInstantiated()。它的执行时机是:
- IoC容器创建对象并执行
refresh()。 - 在
finishBeanFactoryInitialization(beanFactory)阶段,初始化所有剩余的单例Bean。- 首先,创建所有的单实例Bean(调用
getBean())。 - 然后,遍历所有创建好的单例Bean,判断其是否实现了
SmartInitializingSingleton接口。如果是,则调用其afterSingletonsInstantiated()方法。
- 首先,创建所有的单实例Bean(调用
二、实例
理论说得再多,不如动手一试。ApplicationListener是监听容器中发布事件的核心接口,它正是Spring事件驱动模型开发的基石。
该接口定义为:public interface ApplicationListener,意味着它可以监听ApplicationEvent及其所有子类事件。
使用起来,通常遵循以下几个步骤:
- 编写监听器:创建一个类来实现
ApplicationListener接口,或者更简单地,在任意Bean的方法上使用@EventListener注解。后者的原理在于,Spring容器中有一个EventListenerMethodProcessor处理器,专门负责解析方法上的这个注解。 - 纳入容器管理:通过
@Component、@Service等注解,将监听器类装配到Spring容器中。 - 监听事件:只要容器中发布了相关事件,注册的监听器就能自动监听到。例如:
ContextRefreshedEvent:当容器刷新完成,所有Bean都完全创建后发布。ContextClosedEvent:当容器关闭时发布。
- 发布事件:在任何可以获取到
ApplicationContext的地方,调用applicationContext.publishEvent()方法,即可发布自定义事件。
1、自定义监听器
首先,我们可以定义一个实现ApplicationListener接口的类。这样,它就能监听所有类型的ApplicationEvent。别忘了用@Component将其装配到Spring容器中。
package com.atguigu.ext; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; @Component public class MyApplicationListener implements ApplicationListener{ //当容器中发布此事件以后,方法触发 @Override public void onApplicationEvent(ApplicationEvent event) { // TODO Auto-generated method stub System.out.println("收到事件:"+event); } }
另一种更灵活的方式是使用@EventListener注解。你可以将它标注在任何一个Bean的方法上。注意,@EventListener(classes={ApplicationEvent.class})中的classes属性可以指定多个要监听的类,用逗号分隔即可。
package com.atguigu.ext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@EventListener(classes={ApplicationEvent.class})
public void listen(ApplicationEvent event){
System.out.println("UserService。。监听到的事件:"+event);
}
}
2、定义配置类,加载IOC容器
接下来,需要一个配置类来扫描并加载这些组件。
@ComponentScan("com.atguigu.ext")
@Configuration
public class ExtConfig {
@Bean
public Blue blue(){
return new Blue();
}
}
3、测试
最后,我们编写测试代码来验证。在测试中,我们不仅会触发Spring内置的容器刷新和关闭事件,还会主动发布一个自定义事件(这里使用了匿名内部类来创建ApplicationEvent)。
package com.atguigu.test;
import org.junit.Test;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.atguigu.ext.ExtConfig;
public class IOCTest_Ext {
@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class);
//发布事件;
applicationContext.publishEvent(new ApplicationEvent(new String("我发布的事件")) {
});
applicationContext.close();
}
}
运行结果:


结果说明:从控制台输出可以清晰看到,Spring容器自身在刷新和关闭时会发布事件,这些事件被我们自定义的监听器成功捕获。同时,我们主动发布的那个自定义事件“我发布的事件”,也同样被监听到了。这充分证明了,无论是Spring内置的事件,还是开发者自定义的事件,只要监听器注册得当,整个事件驱动模型就能顺畅运转。
热门专题
热门推荐
微软调整XGP战略:降价与《使命召唤》延期入库的背后 最近游戏圈有个大消息:微软宣布下调Xbox Game Pass Ultimate和PC Game Pass的月度订阅价格。具体来看,Ultimate档位从每月29 99美元降到了22 99美元,PC Game Pass则从16 49美元降至13
2026年,Xbox新掌门的第一把火:Game Pass要变“自助餐”了 2026年2月,阿莎·夏尔马接棒菲尔·斯宾塞,成为Xbox的新任CEO。这位新官上任,动作可谓雷厉风行。就在昨天,她点燃了第一把火:Xbox Game Pass Ultimate的月费,从29 99美元直接降到了22 99美元
当明星演员想开游戏工作室:资深同行为何直言“别这么做”? 最近,游戏圈里发生了一场有趣的隔空对话。为《最后生还者》《死亡搁浅》等大作献声的知名演员特洛伊·贝克,在采访中透露了一个雄心勃勃的计划:他想创立自己的游戏工作室,去讲述“自己的故事”。他甚至提到,自己的灵感来源之一,正是曾为《刺客信条:起源》
Steam新款手柄评测视频意外流出,定价信息同步曝光 游戏硬件圈最近有个不大不小的“意外”。根据海外多个科技消息源的报道,Valve即将推出的新款Steam Controller手柄,其评测视频竟然提前在网上泄露了。更关键的是,视频里还直接公布了这款产品的售价:99美元。 事情是这样的:一个名为“T
此前,外网消息源透露,目前PlayStation在PS4和PS5的数字版游戏中加入了DRM验证(正版在线验证)机制。 前情提要>> 简单来说,这个新机制的效果是这样的:从今往后,如果你通过数字商店购买新游戏,那么主机就必须定期连接到PSN网络进行正版验证。具体规则是,如果主机连续超过30天处于离线状





