Spring事件监听

1 背景

​ 在业务开发中,经常是多人协同开发。常有”业务A完成后,需要通知业务B”的场景,最平常的写法就是直接在业务A后面增加业务B的调用。这样的缺点就是业务逻辑耦合,一旦业务C也需要业务A的结果时,就不得不在业务A后面新增调用业务C的代码。

​ 针对这种监听事件,第一时间想到的就是”观察者模式”。使用该模式,新增业务C监听的时候,不需要改动业务A的代码。做到了业务代码的解耦。

​ Spring重写了Java自带的事件监听。

org.springframework.context.ApplicationEvent 继承 java.util.EventObject

org.springframework.context.ApplicationListener 继承 java.util.EventListener

2 使用

​ springframework 中有许多内建的 ApplicationEvent,这里就不做过多的描述,主要针对自定义事件的使用。

2.1 自定义事件监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public static void main(String[] args) {
// 创建 Spring 应用上下文 GenericApplicationContext
GenericApplicationContext context = new GenericApplicationContext();
// 注册 ApplicationListener<MyApplicationEvent> 实现 MyApplicationListener
context.registerBean(MyApplicationListener.class); // registerBean 方法从 Spring 5 引入
// 初始化上下文
context.refresh();
// 发布自定义事件 MyApplicationEvent
context.publishEvent(new MyApplicationEvent("Hello World"));
// 关闭上下文
context.close();
// 再次发布事件
context.publishEvent(new MyApplicationEvent("Hello World Again"));
}

public static class MyApplicationEvent extends ApplicationEvent {
public MyApplicationEvent(String source) {
super(source);
}
}

public static class MyApplicationListener implements ApplicationListener<MyApplicationEvent> {
@Override
public void onApplicationEvent(MyApplicationEvent event) {
System.out.println(event.getClass().getSimpleName());
}
}

上述是测试Demo,注意:ApplicationListener 必须是 Spring 管理的 Bean,并且监听方法必须是 public

正式项目中,可以按照如下格式使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Component
public class MyApplicationListener implements ApplicationListener<MyApplicationEvent> {
@Override
public void onApplicationEvent(MyApplicationEvent event) {
System.out.println(event.getClass().getSimpleName());
}
}

public class MyApplicationEvent extends ApplicationEvent {
public MyApplicationEvent(String source) {
super(source);
}
}

@Service
public class MyApplicationEventService{
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
public void publishEvent(){
applicationEventPublisher.publishEvent(new MyApplicationEvent("Hello MyApplicationEvent"));
}
}

2.2 注解实现自定义事件监听

​ 自Spring4.2引入了 org.springframework.context.event.EventListener 注解。使用注解的话,上面中的 MyApplicationListener, 就可以按照如下改写。

1
2
3
4
5
6
7
@Component
public class MyApplicationListener{
@EventListener(MyApplicationEvent.class)
public void onApplicationEvent(MyApplicationEvent event) {
System.out.println(event.getClass().getSimpleName());
}
}

​ 并且,@EventListener 还支持继承,可以在抽象类上使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public abstract class AbstractEventListener {
@EventListener(ContextRefreshedEvent.class)
public void onContextRefreshedEvent(ContextRefreshedEvent event) {
System.out.println("AbstractEventListener : " + event.getClass().getSimpleName());
}
}

public class MyEventListener extends AbstractEventListener {
@EventListener(ContextClosedEvent.class)
public boolean onContextClosedEvent(ContextClosedEvent event) {
System.out.println("MyEventListener : " + event.getClass().getSimpleName());
return true;
}
}

​ 同时,@EventListener 还支持多事件监听。

1
2
3
4
5
6
7
8
9
10
11
12
13
public static class MyMultiEventsListener {
// 无参数监听 {@link ContextRefreshedEvent} 和 {@link ContextClosedEvent} 事件
@EventListener({ContextRefreshedEvent.class, ContextClosedEvent.class})
public void onEvent() {
System.out.println("onEvent");
}

// 单一 {@link ApplicationContextEvent} 参数监听 {@link ContextRefreshedEvent} 和 {@link ContextClosedEvent} 事件
@EventListener({ContextRefreshedEvent.class, ContextClosedEvent.class})
public void onApplicationContextEvent(ApplicationContextEvent event) {
System.out.println("onApplicationContextEvent : " + event.getClass().getSimpleName());
}
}

ApplicationContextEvent 同时是以上两个类的父类。

2.3 泛型事件监听

​ Spring还提供了针对泛型事件监听。泛型事件,必须实现org.springframework.core.ResolvableTypeProvider

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* 泛型事件
*
* @param <T> 泛型类型
*/
public class GenericEvent<T>
extends ApplicationEvent implements ResolvableTypeProvider {

public GenericEvent(T source) {
super(source);
}

@Override
public ResolvableType getResolvableType() {
return ResolvableType.forClassWithGenerics(getClass(),
ResolvableType.forInstance(getSource()));
}

@Override
public T getSource() {
return (T) super.getSource();
}
}

public class UserEventListener implements ApplicationListener<GenericEvent<User>> {

@EventListener
public void onUser(User user) {
System.out.println("onUser : " + user);
}

@EventListener
public void onUserEvent(GenericEvent<User> event) {
System.out.println("onUserEvent : " + event.getSource());
}

@Override
public void onApplicationEvent(GenericEvent<User> event) {
System.out.println("onApplicationEvent : " + event.getSource());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

public static void main(String[] args) {
// 创建 注解驱动 Spring 应用上下文
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 注册 UserEventListener,即实现 ApplicationListener ,也包含 @EventListener 方法
context.register(UserEventListener.class);
// 初始化上下文
context.refresh();
// 构造泛型事件
GenericEvent<User> event = new GenericEvent(new User("用户1"));
// 发送泛型事件
context.publishEvent(event);
// 发送 User 对象作为事件源
context.publishEvent(new User("用户2"));
// 关闭上下文
context.close();
}

​ 针对泛型事件,在通知的时候,可以直接传入 泛型对象,也可以构建GenericEvent对象。

2.4 异步事件监听

使用 @Async 注解可以快速实现异步调用。当然,需要提前使用 @EnableAsync ,开启异步功能。

1
2
3
4
5
6
7
8
9
@EnableAsync // 需要激活异步,否则 @Async 无效
public class MyAsyncEventListener {
@EventListener(ContextRefreshedEvent.class)
@Async
public Boolean ontextRefreshedEvent(ContextRefreshedEvent event) {
println(" MyAsyncEventListener : " + event.getClass().getSimpleName());
return true;
}
}

也可以实现实现 org.springframework.scheduling.annotation.AsyncConfigurer自定义异步调用的线程池。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Configuration
@EnableAsync
public class AopConfig implements AsyncConfigurer {

@Override
public Executor getAsyncExecutor() {
//线程池
ThreadPoolExecutor poolExecutor = ThreadPoolGenerator.newCallerRunSyncQueuePool("AsyncThreadPool", 20, 50);
return poolExecutor;
}

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}

3 分析

3.1 Spring 事件发布

3.1.1 ApplicationEventMulticaster 注册 ApplicationListener

​ 在org.springframework.context.ApplicationListener的注释上,ApplicationListener是依赖java.util.EventListener的 Observer 设计模式(观察者模式)。并,关联了org.springframework.context.event.ApplicationEventMulticaster接口。

1
2
3
4
5
6
7
8
9
10
// Application Event 多路广播 
public interface ApplicationEventMulticaster {
void addApplicationListener(ApplicationListener<?> listener);
void addApplicationListenerBean(String listenerBeanName);
void removeApplicationListener(ApplicationListener<?> listener);
void removeApplicationListenerBean(String listenerBeanName);
void removeAllListeners();
void multicastEvent(ApplicationEvent event);
void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
}

从以上接口看,ApplicationEventMulticaster的主要职责是:

  1. 关联 ApplicationListener
  2. 广播 ApplicationEvent

​ 从ApplicationEventMulticaster的实现类来看,只有一个抽象类org.springframework.context.event.AbstractApplicationEventMulticaster和唯一实现类org.springframework.context.event.SimpleApplicationEventMulticaster

​ 在抽象类AbstractApplicationEventMulticaster中,你会发现有两个成员变量defaultRetrieverretrieverCacheorg.springframework.context.event.AbstractApplicationEventMulticaster.ListenerCacheKeyorg.springframework.context.event.AbstractApplicationEventMulticaster.ListenerRetriever都是AbstractApplicationEventMulticaster的内部类。

1
2
3
4
5
6
public abstract class AbstractApplicationEventMulticaster
implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);
final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);
......
}

​ 内部类ListenerCacheKey包含了两个属性,sourceTypeeventType

1
2
3
4
5
6
private static final class ListenerCacheKey implements Comparable<ListenerCacheKey> {
private final ResolvableType eventType;
@Nullable
private final Class<?> sourceType;
.....
}

​ 内部类ListenerRetriever包含这ApplicationListener的Set。

1
2
3
4
5
6
private class ListenerRetriever {
public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
private final boolean preFiltered;
...
}

​ 这时候,就知道
AbstractApplicationEventMulticaster#defaultRetriever维护这所有的ApplicationListener;
AbstractApplicationEventMulticaster#retrieverCache 是以ListenerCacheKey为key,ListenerRetriever 为Value 的 Map。并且 ListenerRetriever 维护着ApplicationListener的Set。

​ 就是将defaultRetriever进行分类,存在在retrieverCache中。

3.1.2 AbstractApplicationEventMulticaster 广播事件

ApplicationEventMulticaster#multicastEvent(ApplicationEvent)方法在SimpleApplicationEventMulticaster中被重写了。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}

SimpleApplicationEventMulticaster在广播的时候,会先判断executor是否为空,为空才同步调用。可以通过SimpleApplicationEventMulticaster#setTaskExecutor方法初始化。

3.1.3 ApplicationEventMulticaster 与 ApplicationContext 之间的关系

​ Spring Framework 官方文档提到开发人员可使用ApplicationEventPublisher发布ApplicationEvent

1
2
3
4
5
6
7
@FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
void publishEvent(Object event);
}

​ 从接口上看,ApplicationEventPublisher貌似和ApplicationEventMulticaster 没有什么关联。只有发布的接口,没有关联ApplicationListener的接口。

​ 不过ApplicationEventPublisherApplicationContext继承,因此,无论哪种ApplicationContext都具备发布ApplicationEvent的能力。

​ 要如何得到ApplicationEventPublisher实例?官方文档提示,实现ApplicationEventPublisherAware接口。也可以直接使用@Autowired注解自动导入。

在 Spring Framework 中,Aware Bean 生命周期回调均有ApplicationContextAwareProcessor处理。

ApplicationEventPublisherApplicationContext继承,自动注入的ApplicationEventPublisher实例,就是ApplicationContext实例。AbstractApplicationContext完全实现了ApplicationEventPublisher接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/** Helper class used in event publishing */
@Nullable
private ApplicationEventMulticaster applicationEventMulticaster;

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
}

ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}

if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}

if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}

ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
if (this.applicationEventMulticaster == null) {
throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
"call 'refresh' before multicasting events via the context: " + this);
}
return this.applicationEventMulticaster;
}

applicationEventMulticaster属性由initApplicationEventMulticaster()方法初始化,并且该方法被refresh()调用。

​ 因此ConfigurableApplicationContext能够发布事件,管理事件,是复用了ApplicationEventMulticaster

总结:

ConfigurableApplicationContext继承 ApplicationEventPublisher

ApplicationEventPublisher拥有发布ApplicationEvent的接口。

AbstractApplicationContext实现 ConfigurableApplicationContext接口,增加成员变量ApplicationEventMulticaster,利用ApplicationEventMulticaster实现ApplicationEventPublisher的功能。

ApplicationEventMulticaster负责管理ApplicationListener

3.2 Spring 自定义事件

​ 根据官方文档的提示,自定义 Spring 事件需要扩展ApplicationEvent接口,然后由ApplicationEventPublisher#publishEvent(java.lang.Object)发布即可。

3.3 Spring 事件监听

​ Spring 事件监听接口 ApplicationListener是事件监听的常见手段。在此基础上,Spring 4.2开始引入@EventListener注解。

3.3.1 ApplicationListener监听原理

​ 根据上面的内容,以知发布事件,调用的是SimpleApplicationEventMulticaster#multicastEvent(ApplicationEvent)。并且SimpleApplicationEventMulticaster可关联Executor实现异步事件广播。然后,SimpleApplicationEventMulticaster默认采用同步广播。根据ApplicationEvent具体类型查找匹配的ApplicationListener列表,然后逐一同步或异步调用ApplicationListener#onApplicationEvent方法,实现ApplicationListener的监听。

3.3.2 @EventListener 方法实现原理

​ 从 Spring Framework 4.2 开始,框架层面在 AnnotationConfigUtils#registerAnnotationConfigProcessors(BeanDefinitionRegistry, Object)方法中增加了@EventListener方法处理的相关Bean的注册:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static final String EVENT_LISTENER_PROCESSOR_BEAN_NAME =
"org.springframework.context.event.internalEventListenerProcessor";
public static final String EVENT_LISTENER_FACTORY_BEAN_NAME =
"org.springframework.context.event.internalEventListenerFactory";

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {

......

if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
}

if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
}

return beanDefs;
}

​ 其中EventListenerMethodProcessor@EventListener方法的生命周期处理器,而DefaultEventListenerFactory则是@EventListener方法与ApplicationListener适配器的工程类。

EventListenerMethodProcessor作为SmartInitializingSingleton接口实现。

3.3.2.1 SmartInitializingSingleton生命周期回调
1
2
3
public interface SmartInitializingSingleton {
void afterSingletonsInstantiated();
}

​ 通过调用关系可以发现,SmartInitializingSingleton#afterSingletonsInstantiated()方法将在DefaultListableBeanFactory#preInstantiateSingletons()方法执行时调用。

​ 同时,从完整的Spring应用上下文生命周期分析,以上preInstantiateSingletons()方法处于AbstractApplicationContext#finishBeanFactoryInitialization(ConfigurableListableBeanFactory)调用周期的最后一个操作,并且也接近于refresh()方法的完成阶段。

3.3.2.2 EventListenerMethodProcessor的实现原理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
public void afterSingletonsInstantiated() {
List<EventListenerFactory> factories = getEventListenerFactories();
ConfigurableApplicationContext context = getApplicationContext();
String[] beanNames = context.getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
......
try {
processBean(factories, beanName, type);
}
......
}
}

protected List<EventListenerFactory> getEventListenerFactories() {
Map<String, EventListenerFactory> beans = getApplicationContext().getBeansOfType(EventListenerFactory.class);
List<EventListenerFactory> factories = new ArrayList<>(beans.values());
AnnotationAwareOrderComparator.sort(factories);
return factories;
}

​ 按照 afterSingletonsInstantiated()方法的生命周期,getEventListenerFactories()方法根据类型在Spring应用上下文中查找所有EventListenerFactory Bean的操作不会引起过早初始化的问题。

EventListenerMethodProcessor#processBean方法相对简单,首先从指定Bean类型中超找所有标注@EventListener的方法,由于AnnotatedElementUtils#findMergedAnnotation语言的作用,所有方法筛选的规则是仅判断Bean中所有public方法是否标注@EventListener

​ 然后,EventListenerMethodProcessor将候选的@EventListener方法集合逐一经过EventListenerFactory实例列表(首参)的匹配。而默认情况下,EventListenerFactory实例列表仅为DefaultEventListenerFactory对象,故将@EventListener方法适配为ApplicationListener对象。

1
2
3
4
5
6
7
8
9
10
11
public class DefaultEventListenerFactory implements EventListenerFactory, Ordered {
......
public boolean supportsMethod(Method method) {
return true;
}

@Override
public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
return new ApplicationListenerMethodAdapter(beanName, type, method);
}
}

​ 因此,@EventListener方法实现的确与ApplicationListener有关。