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等方法也是同樣的道理。