【Spring】对 Spring 事件机制及其使用做个小总结 ApplicationEvent ApplicationListener @EventListener
本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前言
Spring 对 JDK 的标准事件 EventObject 做了拓展比如各种 ApplicationEvent,并支持基于 ApplicationEvent 和 @EventListener 来监听对应的事件
Spring IoC Container 通过事件组播器 ApplicationEventMulticaster 来负责 ApplicationListener 的维护和事件的发布,这些细节可以在下文了解:
(【源码】Spring —— ApplicationEvent ApplicationListener ApplicationEventMulticaster - 掘金 (juejin.cn))
本文可以理解为上文的一个延申,主要陈述如下内容:
- 基于
ApplicationListener接口的自定义监听器拓展 - 基于
@EventListener注解方法的自定义监听器拓展
基于 ApplicationListener
demo
@Component
public class CustomListener implements SmartApplicationListener {
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return CustomEvent.class.isAssignableFrom(eventType);
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("listen a custom event");
}
}
@Test
public void test() {
AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext("com.example.springdemoall.beanfactory.event.applicationlistener");
context.publishEvent(new CustomEvent(context, "default"));
}
- 这种方式很简单且容易理解,注册一个
ApplicationListener的组件即可 - 容器会自动感知并注册这个
ApplicationListener,这主要发生在容器启动时的AbstractApplicationContext#registerListeners阶段
AbstractApplicationContext#registerListeners
protected void registerListeners() {
// 这里是注册手动 add 的 ApplicationListener
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// 这里是收集容器中所有注册的 ApplicationListener 的 【beanName,即不会初始化】
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
// 然后以 addApplicationListenerBean 的方式注册
// 发布对应事件时才会初始化
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// ...
}
- 容器启动
refresh阶段的registerListeners方法会收集我们自定义ApplicationListener的beanName - 以
ApplicationEventMulticaster#addApplicationListenerBean形式即注册beanName,这样在发布事件时才会初始化对应的ApplicationListener
基于 @EventListener
@EventListener
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EventListener {
@AliasFor("classes")
Class<?>[] value() default {};
/**
* 处理事件的类,单个类型是接受对应的参数,多个类型
* 时不接受参数
*/
@AliasFor("value")
Class<?>[] classes() default {};
/**
* 基于 SpEL 的条件表达式,上下文包含如下变量:
* #root.event:事件对象
* #root.args:方法参数
* #root.args[0] args[0] #a0 #p0:基于下标获取参数
*/
String condition() default "";
String id() default "";
}
- 我们还可以用
@EventListener注解普通方法的方式来注册ApplicationListener而不实现对应接口 - 监听单个事件时可以在方法参数指定对应事件类型
- 也可以监听多个事件,但不能指定参数
- 支持基于
SpEL的condition表达式,并拥有独立的上下文(具体可见方法注释) - 还可以通过指定方法返回值类型在处理完事件后发布新的对应类型的事件
demo
@Component
public class CommonComponent {
/**
* 基于 SpEL 的条件表达式指定
* 这里是要事件的属性 name == default 才监听
* 单个事件的监听可以指定事件类型的参数,这里是监听 CustomEvent
* 方法返回值是事件类型时,会在处理完当前事件后再发布对应的事件,
* 比如这里处理完 CustomEvent 会发布一个 ChangeEvent
*/
@EventListener(condition = "#event.name == 'default'")
public ChangeEvent listenCustomEvent(CustomEvent event) {
System.out.println("listen a custom event");
return new ChangeEvent(event.getSource());
}
// 监听 ChangeEvent
@EventListener
public void listenChangeEvent(ChangeEvent event) {
System.out.println("listen a change event");
}
// 可以指定监听多个事件,但不能有参数
@EventListener(classes = { CustomEvent.class, ChangeEvent.class })
public void listenBothEvent() {
System.out.println("listen any event");
}
}
@Test
public void test() {
AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext("com.example.springdemoall.beanfactory.event.eventlistener");
context.publishEvent(new CustomEvent(context, "default"));
System.out.println("=======================");
context.publishEvent(new CustomEvent(context, "other"));
}
- 方法
listenCustomEvent基于condition表达式选择监听对应的CustomEvent并在处理后发布新的ChangeEvent - 方法
listenChangeEvent监听所有ChangeEvent - 方法
listenBothEvent监听所有CustomEvent和ChangeEvent
浅析原理
Spring容器启动时会注册的一些内置组件比如EventListenerMethodProcessorDefaultEventListenerFactoryEventListenerMethodProcessor是一个BeanFactoryPostProcessor,它在postProcessBeanFactory会收集对应的EventListenerFactory,默认就是DefaultEventListenerFactory- 同时
EventListenerMethodProcessor也是一个SmartInitializingSingleton,在afterSingletonsInstantiated阶段它会把对应@EventListener注解的方法转换成ApplicationListener注册 DefaultEventListenerFactory就负责把@EventListener注解的方法转换成ApplicationListener,其本质是基于适配器模式把对应方法适配成ApplicationListenerMethodAdapter
总结
Spring 的事件机制小巧而强大,比如 Spring Boot 就基于该机制大作文章拓展了十分强大的能力