spring boot原理分析(七):spring boot运行时事件的监听
前言
在原理分析(六)介绍spring boot启动流程中涉及到的组件或者模块的准备,事件监听器就是其中的一块。事件监听器的运行主要包括三个部分。首先第一部分是用来处理事件的监听器的初始化,这是在SpringApplication的构造函数中完成的。
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
......
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
......
}
第二部分是构造并初始化了事件监听器的管理器列表,这一步是在SpringApplication的run方法调用getRunListeners实现。
public ConfigurableApplicationContext run(String... args) {
......
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
......
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
......
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
......
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
......
listeners.running(context);
......
return context;
}
最后一个部分是事件监听器的管理器发送各种事件,这些操作是分布在run方法和其他run方法中调用的传入listeners的方法中。但是spring boot的事件监听不仅仅局限在SpringApplication中SpringApplicationRunListeners。无论是环境environment还是上下文context的准备(prepare)方法中都只是调用却没有持有listeners,这是因为context自己初始化和构造了另一套事件监听系统。虽然这部分内容属于上下文context的相关部分,但是也会在补充介绍一些。
事件
事件基础定义
//SpringApplicationEvent.java
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;
}
}
//ApplicationEvent.java
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;
}
}
spring boot中事件都是继承自SpringApplicationEvent的抽象类,命名显示这类事件主要是反馈spring boot应用运行时的状态的改变。SpringApplicationEvent持有了一个字符串用来存储应用传入的参数,除此之外,就是继承了ApplicationEvent抽象类。ApplicationEvent持有一个时间戳,存储事件对象被构造的时间。构造方法传入的Object source代表事件被发出的类,这个Object对象是由父类EventObject持有的。需要注意的是,ApplicationEvent是由spring-context jar包提供的,这意味着这种事件处理的模式是spring-context提供的能力。
事件的类型
事件的类型可以由ApplicationEvent的子类例举出来,当然上文提到SpringApplicationEvent也是子类之一。
上图展示了ApplicationEvent一些子类。虽有些并不是处于spring boot包,这也无伤大雅。根据图片可以很容易将这些子类分门别类,有些子类的名字也比较露骨,几乎一眼能识别出主要在什么情况下发挥作用。
- SpringApplicationEvent
SpringApplicationEvent被使用在SpringApplication类中,用来反馈应用启动状态变化的系列事件。当spring boot在启动过程中,应用状态发生变化时,相应的子类Event就会被发出,开发者可以通过编写事件处理器,在合适的阶段插入自己的代码。比如我有一篇文章讲了使用bytebuddy解决spring AOP嵌套方法不生效的问题,代码注入需要在上下文准备之前,所以需要监听ApplicationEnvironmentPreparedEvent。
下面依次介绍每个事件抛出具体场景。在上面run方法中,其实已经出现了大部分的事件。listeners.starting()方法抛出了ApplicationStartingEvent事件,代表应用正在启动中;prepareEnvironment方法中传入了listeners,抛出了ApplicationEnvironmentPreparedEvent,代表环境已经准备就绪。prepareContext方法中抛出了两个事件,
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
......
listeners.contextLoaded(context);
}
其中listeners.contextPrepared抛出了ApplicationContextInitializedEvent代表上下文已经初始化完成,listeners.contextLoaded抛出了ApplicationPreparedEvent代表上下文加载完成,应用准备完毕;
接着run方法中调用listeners.started抛出ApplicationStartedEvent,代表应用启动准备完成;在try catch中的handleRunFailure方法传入了listeners,内部调用了listeners.failed方法,这个毫无疑问是抛出了ApplicationFailedEvent事件,代表应用启动失败;最后run方法中调用listeners.running方法,抛出了ApplicationReadyEvent事件,代表应用启动成功。
-
ApplicationContextEvent
ApplicationContextEvent系列的事件是用来反馈上下文加载过程中状态的变化,主要是在AbstractApplicationContext上下文抽象类中被子类调用并发送事件。同理,根据事件的名称也容易判断事件什么时候被抛出,具体内容还是放在context相关的文章中详细解释。 -
ParentContextAvailableEvent
这个事件用来反馈存在父上下文并且已经准备就绪。 -
WebServerInitializedEvent
这个系列的两个事件,看了在原理分析(六)应该有印象,这里分别代表SERVLET、REACTIVE的web服务已经初始化完成。 -
其他
RequestHandledEvent系列是处理请求完成后会发成的事件。
DataSourceSchemaCreatedEvent是和数据库相关的,当DataSourceSchema创建完成后会抛出这个事件。
ExitCodeEvent是当应用退出时,产生了退出码之后会抛出的事件,可以根据退出码判断应用退出的原因。
JobExecutionEvent是job执行发出的事件,这里的JobExcution是spring batch中提供的。
PayloadApplicationEvent不是在特定情况下被发出,只是当需要传递一个负载信息(Payload)给特定处理器时,可以使用这个事件。PayloadApplicationEvent具有比较强的通用性。
事件的处理
注册一个事件监听的处理器
public class AppEnvListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent applicationEnvironmentPreparedEvent) {
}
}
ApplicationListener是一个接口模板,泛型参数需要传入这个监听器监听的事件,比如这里监听ApplicationEnvironmentPreparedEvent。ApplicationListener接口中就只有一个方法onApplicationEvent,在相应监听事件被发出时,就会调用子类的onApplicationEvent方法,传入对应的事件实例。在实现onApplicationEvent方法时,可以使用事件实例中包含的参数,比如ApplicationEnvironmentPreparedEvent就会包含环境Environment的实例。
最后,不要忘记,要使这个监听器生效,需要在spring.factories中注册。
org.springframework.context.ApplicationListener=\
com.xiaojukeji.epower.aegis.utils.context.AppEnvListener
关于spring.factories文件中的配置能够生效的原理已经在原理分析(三)和原理分析(四)中介绍过了。
事件监听的处理器如何处理事件
事件的发送和处理是由ApplicationEventMulticaster来实现的,这个组件同样也是由spring-context包提供的。
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子类中需要维护注册的ApplicationListener的集合,当抛出事件时使用multicastEvent方法传入事件。其子类AbstractApplicationEventMulticaster也确实是这样做的,只是使用ListenerRetriever对ApplicationListener进行了封装。
public abstract class AbstractApplicationEventMulticaster
implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
.....
private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);
final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);
.....
}
ApplicationEventMulticaster类中包含一个默认的ListenerRetriever和一个键值是ListenerCacheKey的ListenerRetriever的map。先看一下ListenerRetriever的定义。
private class ListenerRetriever {
public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
private final boolean preFiltered;
public ListenerRetriever(boolean preFiltered) {
this.preFiltered = preFiltered;
}
public Collection<ApplicationListener<?>> getApplicationListeners() {
......
return allListeners;
}
}
ListenerRetriever比较简单,就是封装了ApplicationListener和ApplicationListener bean name的集合,实现getApplicationListeners方法能够返回所有的ApplicationListener。那么ListenerCacheKey是什么呢?这个key其实就是对ListenerRetriever进行分类,生成标准是事件的类型和事件产生的类的类型(event type and source type),这样在处理事件时,可以加快搜索对应的ApplicationListener。
最后spring boot中使用的ApplicationEventMulticaster的实现类是SimpleApplicationEventMulticaster,
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
@Nullable
private Executor taskExecutor;
@Nullable
private ErrorHandler errorHandler;
......
@Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
}
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
......
}
其允许传入Executor taskExecutor和ErrorHandler errorHandler用来异步执行Listener对事件的响应和错误的处理。multicastEvent就是用来抛出事件,并调用相应的Listener来处理事件的方法。
ApplicationContextEvent事件监听实例
ApplicationContextEvent的事件监听处理器Listeners在SpringApplication的构造函数获取并初始化,这在前言中已经说过。接下来在run方法中,事件的发送和处理借用的是SpringApplicationRunListeners。
class SpringApplicationRunListeners {
......
private final List<SpringApplicationRunListener> listeners;
......
}
SpringApplicationRunListeners其实只是对SpringApplicationRunListener的列表封装了一下,作为代理所有操作还是遍历每一个SpringApplicationRunListener来处理的。
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
private final SpringApplication application;
private final String[] args;
private final SimpleApplicationEventMulticaster initialMulticaster;
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
......
}
事实上,默认情况下,继承自SpringApplicationRunListener的子类只有一个,就是EventPublishingRunListener。也是在这个子类中,构造了SimpleApplicationEventMulticaster的实例,并将注册的事件监听处理器Listener传了进去,用来发送和处理事件。比如对应run方法中的starting()方法,这里就直接使用initialMulticaster.multicastEvent处理了ApplicationStartingEvent的事件。对应environmentPrepared、contextPrepared、started等方法也是同样的道理。