SpringBoot 事件原理分析
简介
通常基于事件编程,我们直接使用ApplicationContext类里面的**publishEvent()**方法进行事件发布。
public void publishEvent(ApplicationEvent event);
ApplicationEvent 是一个事件类,我们可以继承该类,发布自己创建的事件类,在自定义一个监听类来监听该事件对象。
public abstract class ApplicationEvent extends EventObject {
private final long timestamp;
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
public final long getTimestamp() {
return this.timestamp;
}
}
监听类需要实现:ApplicationListener 接口里面的 onApplicationEvent 方法。
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
}
实现类要注入到容器里面。
这其实类似设计模式里面的发布订阅者模式。
SpringBoot 的事件是基于Spring的事件,所以我们先介绍Spring事件原理。
1.Spring 事件原理
核心类:
- ApplicationEventMulticaster,事件派发器。
- ApplicationListener,事件监听类。
- ApplicationEvent,事件类。
事件派发器派发事件,事件监听类监听派发的事件。
1.ApplicationEventMulticaster事件派发器的注册时机,何时被注册到Spring容器内部的?
我们找到Spring容器创建Bean的流程中,AbstractApplicationContext#refresh这个方法里:
// Initialize message source for this context.
initMessageSource();
// 在这一步骤中,初始化了事件派发器
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
initApplicationEventMulticaster方法:
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
...
// 这一步骤创建了派发器
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
// 注册到单例池里面
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
...
}
2.当我们调用了publishEvent(ApplicationEvent event);监听类怎么就会执行了呢?
我们点进该方法里面AbstractApplicationContext#publishEvent():
// 会调用ApplicationEventMulticaster的multicastEvent()方法
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
ApplicationEventMulticaster#multicastEvent():
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
// 从派发器里面获取线程池对象
Executor executor = getTaskExecutor();
// getApplicationListeners(event, type) 或获取该事件对象类型的所有监听器
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
// 线程池异步调用
executor.execute(() -> invokeListener(listener, event));
}
else {
// 直接调用
invokeListener(listener, event);
}
}
}
invokeListener方法:
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
// 这个直接调用 listener.onApplicationEvent(event); 方法
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}
总结:当我们调用 applicationContext.publishEvent(xxx);方法时,Spring内部会拿到关于此事件对象的所有监听器类对象,直接调用执行。
2.SpringBoot 启动事件流程
SpringBoot在启动时,会默认发布一些事件,我们可以自定义监听类实现监听该事件,做一些初始化等等操作,一些框架整合就是通过事件监听的方式进行内部的初始化。
值得注意的是SpringBoot对于有些事件的监听是只能通过读取配置里面配置的监听类才能生效,直接注解的方式是无法监听到的!
为什么会这样呢?
因为我们直接加注解的话,是必须要经过Spring容器的洗礼,有些事件的发布是在容器refresh之前做的,所以注解的方式是没办法生效的!
SpringBoot提供了配置文件的方式去配置监听器,那么配置文件中的实现类是何时获取到的呢?
在构造器里面获取的,SPI机制加载实现类。
我们要监听一些容器刷新之前的内部事件只能在spring.factories中指定。
启动时会发布的事件顺序:
- ApplicationStartingEvent: 准备启动SpringBoot环境之前。
- ApplicationEnvironmentPreparedEvent: 环境变量初始化完成,应用启动之前。
- ApplicationContextInitializedEvent:应用初始化器执行后发布。
- ApplicationPreparedEvent:应用准备就绪,容器初始化之前发布。
- ApplicationStartedEvent:容器初始化完成之后发布。
- ApplicationReadyEvent:容器已经初始化完成并且已经可以接收Web请求之后发布。
- ApplicationFailedEvent:容器启动失败。
除此之外,在ApplicationPreparedEvent之后和ApplicationStartedEvent之前还将发布以下事件:
- ContextRefreshedEvent:容器刷新完成发布。
- WebServerInitializedEvent :在WebServer准备就绪后发送。ServletWebServerInitializedEvent和ReactiveWebServerInitializedEvent分别是servlet和reactive变体。
以上的事件对象都继承 SpringApplicationEvent 类,SpringApplicationEvent 又继承 ApplicationEvent。
public abstract class SpringApplicationEvent extends ApplicationEvent {
private final String[] args;
public SpringApplicationEvent(SpringApplication application, String[] args) {
super(application);
this.args = args;
}
public SpringApplication getSpringApplication() {
return (SpringApplication) getSource();
}
public final String[] getArgs() {
return this.args;
}
}
以上事件的发布类是:EventPublishingRunListener。
EventPublishingRunListener 类是SpringBoot类似通过SPI机制加载进来的:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
...
// 这一步加载进来的
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
}
EventPublishingRunListener 实现了 SpringApplicationRunListener接口,该接口里面规范了SpringBoot启动时机的事件发布流程。
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
private final SpringApplication application;
// 事件派发器对象
private final SimpleApplicationEventMulticaster initialMulticaster;
....
}
以上就是SpringBoot启动时的事件发布流程。