一、原理及源码解析
在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内置的事件,还是开发者自定义的事件,只要监听器注册得当,整个事件驱动模型就能顺畅运转。
