【Spring】对 Spring 事件机制及其使用做个小总结 ApplicationEvent ApplicationListener @EventListener
前言
Spring
对 JDK
的标准事件 EventObject
做了拓展比如各种 ApplicationEvent
,并支持基于 ApplicationEvent
和 @EventListener
来监听对应的事件
Spring IoC Container
通过事件组播器 ApplicationEventMulticaster
来负责 ApplicationListener
的维护和事件的发布,这些细节可以在下文了解:
【源码】Spring —— ApplicationEvent ApplicationListener ApplicationEventMulticaster
本文可以理解为上文的一个延申,主要陈述如下内容:
- 基于
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
容器启动时会注册的一些内置组件比如EventListenerMethodProcessor
DefaultEventListenerFactory
EventListenerMethodProcessor
是一个BeanFactoryPostProcessor
,它在postProcessBeanFactory
会收集对应的EventListenerFactory
,默认就是DefaultEventListenerFactory
- 同时
EventListenerMethodProcessor
也是一个SmartInitializingSingleton
,在afterSingletonsInstantiated
阶段它会把对应@EventListener
注解的方法转换成ApplicationListener
注册 DefaultEventListenerFactory
就负责把@EventListener
注解的方法转换成ApplicationListener
,其本质是基于适配器模式
把对应方法适配成ApplicationListenerMethodAdapter
总结
Spring
的事件机制小巧而强大,比如 Spring Boot
就基于该机制大作文章拓展了十分强大的能力