一文读懂Spring事件机制
Spring 事件机制是为同一ApplicationContext
中多个Bean
之间的通信而设计的,它是观察者模式的一个典型应用。
观察者模式在生活中随处可见,比如:在安装有交通信号灯的路口,行人需要实时观察信号灯的变化,从而判断是否需要前进、缓行或等待;而信号灯只负责发布信号变更事件,并不会干预行人,也就是说信号灯与行人之间是松耦合的关系。
为什么要搞清楚 Spring 事件机制呢?因为这玩意儿在 Spring 内部应用广泛,不搞清楚的话,看 Spring 相关源码会是一个拦路虎;此外,也可以在实际工作中解决相关问题,比如:笔者在工作中曾借助它来实现流程节点的自动流转,这些业务流程往往很长,但只有少数流程节点需要客户手动触发完成,其余节点需要自动流转。
1 从实战出发
聊到事件机制,那就不得不提事件处理模型三要素了,即事件源、事件和事件监听器。
1.1 定义事件
在 Spring 4.2 之前,所有事件均需要继承自ApplicationEvent
,而 ApplicationEvent 又继承自java.util.EventObject
;但在 Spring 4.2 之后,并不强制要求自定义事件继承 ApplicationEvent,因为 Spring 会将开发者自定义的事件类包装为一个PayloadApplicationEvent
,其实,PayloadApplicationEvent 同样继承自 ApplicationEvent。
1.2 定义事件监听器
// 事件监听器,即行人 | |
public class CustomTrafficSignalListener implements ApplicationListener<TrafficSignalEvent> { | |
public void onApplicationEvent(TrafficSignalEvent trafficSignalEvent) { | |
if (trafficSignalEvent instanceof RedSignalEvent) { | |
log.info("红灯亮,请所有行人和车辆停止前行"); | |
} else if (trafficSignalEvent instanceof GreenSignalEvent) { | |
log.info("绿灯亮,请所有行人和车辆前行"); | |
} else if (trafficSignalEvent instanceof YellowSignalEvent) { | |
log.info("黄灯亮,请所有行人和车辆缓行"); | |
} else { | |
throw new IllegalStateException("未知的信号灯事件"); | |
} | |
} | |
} |
Spring 4.2 提供了org.springframework.context.event.EventListener
注解,不再需要实现org.springframework.context.ApplicationListener
接口。相信大家在实际工作中更倾向于使用@EventListener
的方式来定义事件监听器。
// 事件监听器,即行人 | |
public class FlexibleTrafficSignalListener { | |
public void onTrafficSignalEvent(TrafficSignalEvent event) { | |
if (event instanceof RedSignalEvent) { | |
log.info("红灯亮,请所有行人和车辆停止前行"); | |
} else if (event instanceof GreenSignalEvent) { | |
log.info("绿灯亮,请所有行人和车辆前行"); | |
} else if (event instanceof YellowSignalEvent) { | |
log.info("黄灯亮,请所有行人和车辆缓行"); | |
} else { | |
throw new IllegalStateException("未知的信号灯事件"); | |
} | |
} | |
} |
1.3 定义事件源
// 交通信号灯,即事件源 | |
public class TrafficSignal implements ApplicationEventPublisherAware { | |
private ApplicationEventPublisher applicationEventPublisher; | |
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { | |
this.applicationEventPublisher = applicationEventPublisher; | |
} | |
public void doSignal() { | |
TrafficSignalEvent redSignalEvent = new RedSignalEvent(this); | |
applicationEventPublisher.publishEvent(redSignalEvent); | |
} | |
} |
1.4 运行结果
CustomTrafficSignalListener
2021-08-16 20:59:19.728 INFO 11908 --- [main] p.d.e.e.CustomTrafficSignalListener : 红灯亮,请所有行人和车辆停止前行
FlexibleTrafficSignalListener
2021-08-16 20:59:22.135 INFO 17653 --- [main] p.d.e.e.FlexibleTrafficSignalListener : 红灯亮,请所有行人和车辆停止前行
2 异步事件监听器
默认情况下,事件监听器基于 同步机制 来处理事件,这意味着ApplicationEventPublisher
的publishEvent()
方法会阻塞,直到所有事件监听器都处理完毕。如果你想指定某一事件监听器基于 异步机制 来处理事件,可以使用@Async
注解。当然,也可以通过配置自定义的ApplicationEventMulticaster
使得所有事件监听器都基于异步机制处理事件。值得一提的是,全局异步监听所涉及的这一自定义ApplicationEventMulticaster
有个坑,即该Bean的名称必须是applicationEventMulticaster
,否则不会生效,具体原因下面会分析。
2.1 配置自定义事件广播器
public class ApplicationEventMulticasterConfig { | |
public ApplicationEventMulticaster applicationEventMulticaster() { | |
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("event-pool-%d").build(); | |
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( | |
0, | |
16, | |
0L, | |
TimeUnit.SECONDS, | |
new LinkedBlockingQueue<Runnable>(24), | |
namedThreadFactory, | |
new ThreadPoolExecutor.AbortPolicy() | |
); | |
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster(); | |
simpleApplicationEventMulticaster.setTaskExecutor(threadPoolExecutor); | |
return simpleApplicationEventMulticaster; | |
} | |
} |
2.2 运行结果
2021-08-16 22:05:50.053 INFO 13652 --- [event-pool-11] p.d.e.e.FlexibleTrafficSignalListener : 红灯亮,请所有行人和车辆停止前行
3 由Spring所发布的预定义事件
Spring 提供了一些预定义的事件,如下图所示:
但有些事件在 ApplicationContext 创建之前就已触发,该如何观测它们呢?
3.1 定义事件监听器
public class SpringBuiltInEventsListener implements ApplicationListener<ApplicationEvent> { | |
public void onApplicationEvent(ApplicationEvent event) { | |
log.info("event = {}", event); | |
} | |
} |
3.2 向SpringApplication注册事件监听器
public class DuXiaoTouSpringAopApp { | |
public static void main(String[] args) throws Throwable { | |
SpringApplication springApplication = new SpringApplication(DuXiaoTouSpringAopApp.class); | |
springApplication.addListeners(new SpringBuiltInEventsListener()); | |
springApplication.run(args); | |
} | |
} |
3.3 运行结果
2021-08-18 17:14:43.182 INFO 15624 --- [ main] p.d.e.e.SpringBuiltInEventsListener : event = org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent[source=org.springframework.boot.SpringApplication@2a5b3fee] | |
2021-08-18 17:14:43.291 INFO 15624 --- [ main] p.d.e.e.SpringBuiltInEventsListener : event = org.springframework.boot.context.event.ApplicationContextInitializedEvent[source=org.springframework.boot.SpringApplication@2a5b3fee] | |
2021-08-18 17:14:43.417 INFO 15624 --- [ main] p.d.e.e.SpringBuiltInEventsListener : event = org.springframework.boot.context.event.ApplicationPreparedEvent[source=org.springframework.boot.SpringApplication@2a5b3fee] | |
2021-08-18 17:14:45.865 INFO 15624 --- [ event-pool-0] p.d.e.e.SpringBuiltInEventsListener : event = org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent[source=org.springframework.boot.web.embedded.tomcat.TomcatWebServer@4ca6ee9b] | |
2021-08-18 17:14:45.872 INFO 15624 --- [ event-pool-0] p.d.e.e.SpringBuiltInEventsListener : event = org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@54d18072, started on Wed Aug 18 17:14:43 CST 2021] | |
2021-08-18 17:14:45.873 INFO 15624 --- [ event-pool-0] p.d.e.e.SpringBuiltInEventsListener : event = org.springframework.boot.context.event.ApplicationStartedEvent[source=org.springframework.boot.SpringApplication@2a5b3fee] | |
2021-08-18 17:14:45.873 INFO 15624 --- [ event-pool-0] p.d.e.e.SpringBuiltInEventsListener : event = org.springframework.boot.availability.AvailabilityChangeEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@54d18072, started on Wed Aug 18 17:14:43 CST 2021] | |
2021-08-18 17:14:45.875 INFO 15624 --- [ event-pool-1] p.d.e.e.SpringBuiltInEventsListener : event = org.springframework.boot.context.event.ApplicationReadyEvent[source=org.springframework.boot.SpringApplication@2a5b3fee] | |
2021-08-18 17:14:45.875 INFO 15624 --- [ event-pool-1] p.d.e.e.SpringBuiltInEventsListener : event = org.springframework.boot.availability.AvailabilityChangeEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@54d18072, started on Wed Aug 18 17:14:43 CST 2021] |
4 实现原理
在 Spring 事件机制中,ApplicationEventPublisher
负责发布事件,其只定义了两个publishEvent()
方法,如下:
public interface ApplicationEventPublisher { | |
default void publishEvent(ApplicationEvent event) { | |
publishEvent((Object) event); | |
} | |
void publishEvent(Object event); | |
} |
ApplicationEventPublisher 的实现类几乎都以ApplicationContext
结尾,这难道有什么玄机吗?Spring 事件机制是为同一ApplicationContext
中多个Bean
之间的通信而设计的,而 ApplicationContext 在 Spring 中扮演IoC容器
的角色,负责Bean全生命周期管理,那发布事件当然交由ApplicationContext最合适了啊;其中最为重要的非AbstractApplicationContext
莫属!废话不多说,上代码:
public abstract class AbstractApplicationContext | |
extends DefaultResourceLoader | |
implements ConfigurableApplicationContext { | |
// 事件广播器的名称必须指定为applicationEventMulticaster,源码中就是这么限定的 | |
// 如果不按规矩来,那自定义的事件广播器屁用没有 | |
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster"; | |
// 事件广播器,负责向所有事件监听器广播事件 | |
private ApplicationEventMulticaster applicationEventMulticaster; | |
private final Object startupShutdownMonitor = new Object(); | |
public void publishEvent(ApplicationEvent event) { | |
publishEvent(event, null); | |
} | |
public void publishEvent(Object event) { | |
publishEvent(event, null); | |
} | |
// 发布事件核心逻辑 | |
protected void publishEvent(Object event, ResolvableType eventType) { | |
ApplicationEvent applicationEvent; | |
if (event instanceof ApplicationEvent) { | |
applicationEvent = (ApplicationEvent) event; | |
} else { | |
// 如果所发布的事件不是ApplicationEvent类型,那么默认将其包装为ApplicationEvent类型 | |
applicationEvent = new PayloadApplicationEvent<>(this, event); | |
if (eventType == null) { | |
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType(); | |
} | |
} | |
// 通过事件广播器广播事件,广播给谁?事件监听器 | |
this.applicationEventMulticaster.multicastEvent(applicationEvent, eventType); | |
} | |
/** | |
* 这个方法容易忽略,请重点关注以下两个方法,相信看了之后应该能解决你的疑惑··· | |
* 1) initApplicationEventMulticaster() | |
* 2) registerListeners() | |
*/ | |
public void refresh() { | |
synchronized (this.startupShutdownMonitor) { | |
// ... | |
// 初始化事件广播器 | |
initApplicationEventMulticaster(); | |
// 注册事件监听器 | |
registerListeners(); | |
// ... | |
} | |
} | |
// 初始化事件广播器 | |
protected void initApplicationEventMulticaster() { | |
ConfigurableListableBeanFactory beanFactory = getBeanFactory(); | |
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) { | |
// 如果开发者自定义了名称为applicationEventMulticaster的事件广播器,则优先使用开发者自定义的 | |
this.applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class); | |
} else { | |
// 如果开发者没有自定义或者定义了名称不是applicationEventMulticaster的事件广播器,则默认使用SimpleApplicationEventMulticaster | |
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory); | |
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster); | |
} | |
} | |
// 注册事件监听器 | |
protected void registerListeners() { | |
// 将事件监听器注册到事件广播器中 | |
// 但这里的事件监听器专指静态事件监听器,即META-INF下spring.factories配置文件中的事件监听器 | |
// 比如:spring-boot、jasypt-spring-boot等模块中均配置了静态的事件监听器 | |
for (ApplicationListener<?> listener : getApplicationListeners()) { | |
this.applicationEventMulticaster.addApplicationListener(listener); | |
} | |
// 同样是将事件监听器注册到事件广播器中 | |
// 但这里主要指那些实现ApplicationListener接口的事件监听器哦 | |
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); | |
for (String listenerBeanName : listenerBeanNames) { | |
this.applicationEventMulticaster.addApplicationListenerBean(listenerBeanName); | |
} | |
} | |
} |
从 AbstractApplicationContext 的源码来看,主要交代了两件事:
- AbstractApplicationContext 是实现事件发布的真正大佬,但发布事件并没有直接与事件监听器耦合,而是通过事件广播器来桥接;
- 当启动 Spring Boot 时会触发针对 ApplicationContext 的
refresh()
操作,而refresh()
首先会初始化事件广播器,然后注册事件监听器;注意:registerListeners()
中所涉及的getApplicationListeners()
方法其实只是直接拿到事件监听器列表而已,真正从spring.factories
配置文件中读取事件监听器的触发时机是在实例化SpringApplication
的时候。眼尖的朋友可能会问:由@EventListener
定义的事件监听器是如何注册到事件广播器中的呢?的确,这里不涉及基于注解模型的事件监听器的注册工作,这些事件监听器由EventListenerMethodProcessor
来完成注册工作,最终会将其封装为ApplicationListenerMethodAdapter
类型的监听器,但依然是ApplicationListener的子类。
接下来,自然要来认识一下ApplicationEventMulticaster
大佬了。事件广播器不仅负责广播事件,同时主导Spring和开发者所定义的事件监听器的维护工作。
public interface ApplicationEventMulticaster { | |
void addApplicationListener(ApplicationListener<?> listener); | |
void addApplicationListenerBean(String listenerBeanName); | |
void removeApplicationListener(ApplicationListener<?> listener); | |
void removeApplicationListenerBean(String listenerBeanName); | |
void removeApplicationListeners(Predicate<ApplicationListener<?>> predicate); | |
void removeApplicationListenerBeans(Predicate<String> predicate); | |
void removeAllListeners(); | |
void multicastEvent(ApplicationEvent event); | |
void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType); | |
} |
Spring 事件机制默认采用SimpleApplicationEventMulticaster
来作为事件广播器,其源码过于直白,这里就直接奉上源码不再解读了。
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster { | |
// 线程池,用于实现异步监听器 | |
private Executor taskExecutor; | |
private ErrorHandler errorHandler; | |
public void multicastEvent(ApplicationEvent event) { | |
multicastEvent(event, resolveDefaultEventType(event)); | |
} | |
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) { | |
ResolvableType type = (eventType != null ? eventType : ResolvableType.forInstance(event); | |
Executor executor = getTaskExecutor(); | |
// 事件广播器会遍历所有事件监听器,逐一向其发布事件 | |
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { | |
if (executor != null) { | |
executor.execute(() -> invokeListener(listener, event)); | |
} else { | |
invokeListener(listener, event); | |
} | |
} | |
} | |
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) { | |
ErrorHandler errorHandler = getErrorHandler(); | |
if (errorHandler != null) { | |
try { | |
doInvokeListener(listener, event); | |
} | |
catch (Throwable err) { | |
errorHandler.handleError(err); | |
} | |
} else { | |
doInvokeListener(listener, event); | |
} | |
} | |
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { | |
try { | |
listener.onApplicationEvent(event); | |
} catch (ClassCastException ex) { | |
throw ex; | |
} | |
} | |
} |
5 总结
6 参考文档
- https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#context-functionality-events
- https://refactoring.guru/design-patterns/observer