Spring ApplicationListener分析
Spring 的核心是 ApplicationContext,它负责管理 Bean的完整生命周期;当加载 Bean 时,ApplicationContext 发布某些类型的事件;例如,当上下文启动时,ContextStartedEvent 发布消息,当上下文停止时,ContextStoppedEvent 发布消息;
通过 ApplicationEvent 类和 ApplicationListener 接口来提供在 ApplicationContext 中处理事件;如果一个 Bean 实现 ApplicationListener 接口,那么每次 ApplicationEvent 被发布到 ApplicationContext 上,那个 Bean实例会被通知;类似MQ的发布订阅;
ApplicationEvent UML图如下:
-
Spring常用事件
序号 | Spring 事件 |
---|---|
1 | ContextRefreshedEvent:ApplicationContext 被初始化或刷新时,该事件被发布;这也可以在 ConfigurableApplicationContext 接口中使用 refresh() 方法来发生;容器刷新完成(所有bean都完全创建)会发布这个事件; |
2 | ContextStartedEvent:当使用 ConfigurableApplicationContext 接口中的 start() 方法启动 ApplicationContext 时,该事件被发布; |
3 | ContextStoppedEvent:当使用 ConfigurableApplicationContext 接口中的 stop() 方法停止 ApplicationContext 时,发布这个事件; |
4 | ContextClosedEvent:当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布; |
5 | RequestHandledEvent:在Web应用中,当一个http请求结束触发该事件 |
AbstractApplicationContext#initApplicationEventMulticaster 用于初始化事件多播器;
AbstractApplicationContext#registerListeners 用于将事件监听器注册到多播器;
-
AbstractApplicationContext#initApplicationEventMulticaster
ApplicationEventMulticaster该接口定义了具体事件监听器的注册管理以及事件发布的方法 ;
ApplicationEventMulticaster有一抽象实现类AbstractApplicationEventMulticaster,它实现了事件监听器的管理 功能;出于灵活性和扩展性考虑,事件的发布功能则委托给了其子类,即ApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType);
SimpleApplicationEventMulticaster 是 Spring 提 供 的 AbstractApplicationEventMulticaster的一个子类实现,添加了事件发布功能的实现;
这里画了三个大框,分别做不同的事情;
-
- 第一个大框,里面处理的是将容器中ApplicationListener的实现类注册到多播器中;
- 第二大个框,里面处理的是将自定义的ApplicationListenr的实现类注册到多播器中;
DefaultListableBeanFactory#doGetBeanNamesForType
自定义ApplicationListener的实现类最终会在这里组装到List集合;
里面调用的AbstractBeanFactory#isTypeMatch,用于判断要查询的beanName的实例与该匹配的类型是否一致;
- 第三个大框,earlyApplicationEvents是早期事件,而早期事件是指多播器未创建完成前的事件,也就AbstractApplicationContext#initApplicationEventMulticaster方法执行前,将没有多播器则将之前的事件保存,之后调用getApplicationEventMulticaster().multicastEvent(earlyEvent)方法发布事件;
-
SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
getTaskExecutor(),多播器中是否支持线程池异步发送事件;
调用SimpleApplicationEventMulticaster#doInvokeListener;
SimpleApplicationEventMulticaster#doInvokeListener
listener.onApplicationEvent(event),调用对于listener的onApplicationEvent事件;
Spring的ApplicationContext容器内的事件发布机制,主要用于单一容器内的简单消息通知和处理,并不适合分布式、多进程、多容器之间的事件通知 ;
可以通过如下两种方式为我们的业务对象注入ApplicationEventPublisher的依赖
-
使用ApplicationEventPublisherAware接口;在ApplicationContext类型的容器启动时,会自动识别该类型的bean定义并将ApplicationContext容器本身作为ApplicationEventPublisher注入当前对象,而ApplicationContext容器本身就是一个ApplicationEventPublisher;
-
使用ApplicationContextAware接口; ApplicationContext本身就是一个ApplicationEventPublisher,通过ApplicationContextAware几乎达到第一种方式相同的效果;
-
ApplicationListener,ApplicationEvent,ApplicationEventMulticaster,ApplicationEventPublisher类关系图
Spring的事件监听是基于观察者模式的,有三部分组成:
-
- 事件(ApplicationEvent):负责对应相应监听器,事件源发生某事件是特定事件监听器被触发的原因;
- 监听器(ApplicationListener):对应于观察者模式中的观察者,监听器监听特定的事件,并在内部定义了事件后发生相应的逻辑;
- 事件发布器(ApplicationEventMulticaster):对应于观察者模式中的被观察者/主题,负责通知观察者对外提供发布事件和增删事件监听器的接口,维护事件和监听器的映射关系,并在事件发生时负责通知相关的监听器;
Spring事件机制是观察者模式的一种实现,但是除了发布者和监听者两个角色外,还有一个EventMultiCaster(事件多播器)的角色负责把事件转发给监听器,在代码中体现:发布者调用applicationEventPublisher.publishEvent(msg); 将事件发送给了EventMultiCaster,之后由EventMultiCaster注册所有的Listener,然后根据事件类型决定转发给哪个Listener;
测试代码如下:
配置类
@Configuration @Import (value = { DemoApplicationListener1. class }) public class ListenerConfig1 { }
事件监听类
查看代码
public class DemoApplicationListener1 implements ApplicationListener {
private final static Logger logger = LoggerFactory
.getLogger(DemoApplicationListener1. class );
@Override
public void onApplicationEvent(ApplicationEvent event) {
logger.info( "接收到一个事件:" + event);
}
}
测试类
查看代码
@Test
public void listenerTest1() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
ListenerConfig1. class );
context.publishEvent( new ApplicationEvent( "发送测试消息" ) {
});
try {
TimeUnit.SECONDS.sleep( 5 );
}
catch (InterruptedException e) {
e.printStackTrace();
}
context.close();
}
运行结果如下:
从日志可以看出,事件监听处理是在主线程,上面说到的多播器可以支持线程池异步发送事件,在配置类直接的@Bean一个线程池对象是行不通的,这里的对象是直接new出来的,它不能够直接给Spring管理;多播器要支持异步具体有下面两种方式;
- 方式一
在AbstractApplicationContext#initApplicationEventMulticaster会判断IOC容器中是否存在beanName为APPLICATION_EVENT_MULTICASTER_BEAN_NAME的多播器对象,即applicationEventMulticaster,因此可以自定义一个多播器并将它的beanName设置为applicationEventMulticaster;
自定义的事件多播器
查看代码
@Component ( "applicationEventMulticaster" )
public class DemoEventMulticaster extends SimpleApplicationEventMulticaster {
public DemoEventMulticaster() {
/**
* @See org.springframework.context.support.AbstractApplicationContext#initApplicationEventMulticaster()
*/
setTaskExecutor(Executors.newCachedThreadPool());
}
}
上面的配置类,@Import部分修改如下:
@Import (value = { DemoApplicationListener1. class , DemoEventMulticaster. class })
运行结果如下:
- 方式二
使用Spring管理的线程池支持事件多播器的异步;
事件监听器
查看代码
@Component
public class DemoApplicationListener2 implements ApplicationListener {
private final static Logger logger = LoggerFactory
.getLogger(DemoApplicationListener2. class );
@Override
@Async
public void onApplicationEvent(ApplicationEvent event) {
logger.info( "接收到一个事件:" + event);
}
}
配置类
查看代码
@EnableAsync
@Configuration
@Import (value = { DemoApplicationListener2. class })
public class ListenerConfig2 implements AsyncConfigurer {
private final static Logger logger = LoggerFactory.getLogger(ListenerConfig2. class );
// 获取线程池
@Override
public Executor getAsyncExecutor() {
// 定义线程池
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
// 设置核心线程数
executor.setCorePoolSize( 4 );
// 设置线程池最大线程数
executor.setMaxPoolSize( 10 );
// 设置线程池队列容量
executor.setQueueCapacity( 1000 );
// 初始化
executor.initialize();
logger.info(String.valueOf(executor));
return executor;
}
}
测试类:
查看代码
@Test
public void listenerTest2() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
ListenerConfig2. class );
context.publishEvent( new ApplicationEvent( "发送测试消息" ) {
});
try {
TimeUnit.SECONDS.sleep( 5 );
}
catch (InterruptedException e) {
e.printStackTrace();
}
context.close();
}
运行结果如下:
也可以使用@EventListener事件注解;
修改上面的事件监听器
查看代码
@Component
public class DemoApplicationListener2 {
private final static Logger logger = LoggerFactory
.getLogger(DemoApplicationListener2. class );
@Async
@EventListener
public void onApplicationEvent(ApplicationEvent event) {
logger.info( "接收到一个事件:" + event);
}
}
运行结果如下: