Spring 事件监听
Posted 街头卖艺的肖邦
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring 事件监听相关的知识,希望对你有一定的参考价值。
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类关系图
测试代码如下:
配置类
@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); } }
运行结果如下:
以上是关于Spring 事件监听的主要内容,如果未能解决你的问题,请参考以下文章