微信公衆號:glmapper工做室
掘金專欄:glmapper
微 博:瘋狂的石頭_henu
歡迎關注,一塊兒學習分享技術java
在這篇文章中聊一聊 Spring 中的擴展機制(一)中對Spring
中的事件機制進行了分析。那麼對於 SpringBoot
來講,它在 Spring
的基礎上又作了哪些拓展呢?本篇未來聊一聊 SpringBoot
中的事件。git
在 SpringBoot 的啓動過程當中,會經過 SPI 機制去加載 spring.factories 下面的一些類,這裏面就包括了事件相關的類。github
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
複製代碼
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
複製代碼
SpringApplicationRunListener
類是 SpringBoot
中新增的類。SpringApplication
類 中使用它們來間接調用 ApplicationListener
。另外還有一個新增的類是SpringApplicationRunListeners
,SpringApplicationRunListeners
中包含了多個 SpringApplicationRunListener
。spring
SpringApplicationRunListener
接口規定了 SpringBoot
的生命週期,在各個生命週期廣播相應的事件,調用實際的 ApplicationListener
類。經過對 SpringApplicationRunListener
的分析,也能夠對 SpringBoot
的整個啓動過程的理解會有很大幫助。緩存
先來看下SpringApplicationRunListener
接口的代碼:bash
public interface SpringApplicationRunListener {
//當run方法首次啓動時當即調用。可用於很是早期的初始化。
void starting();
//在準備好環境後,但在建立ApplicationContext以前調用。
void environmentPrepared(ConfigurableEnvironment environment);
//在建立和準備好ApplicationContext以後,但在加載源以前調用。
void contextPrepared(ConfigurableApplicationContext context);
//在加載應用程序上下文後但刷新以前調用
void contextLoaded(ConfigurableApplicationContext context);
//上下文已刷新,應用程序已啓動,但還沒有調用commandlinerunner和applicationrunner。
void started(ConfigurableApplicationContext context);
//在運行方法完成以前當即調用,此時應用程序上下文已刷新,
//而且全部commandlinerunner和applicationrunner都已調用。
//2.0 纔有
void running(ConfigurableApplicationContext context);
//在運行應用程序時發生故障時調用。2.0 纔有
void failed(ConfigurableApplicationContext context, Throwable exception);
}
複製代碼
上面提到,SpringApplicationRunListeners
是SpringApplicationRunListener
的集合,裏面包括了不少SpringApplicationRunListener
實例;SpringApplication
類實際上使用的是 SpringApplicationRunListeners
類,與 SpringApplicationRunListener
生命週期相同,調用各個週期的 SpringApplicationRunListener
。而後廣播相應的事件到 ApplicationListener
。微信
代碼詳見:SpringApplicationRunListeners.併發
EventPublishingRunListener
類是 SpringApplicationRunListener
接口的實現類 ,它具備廣播事件的功能。其內部使用 ApplicationEventMulticaster
在實際刷新上下文以前發佈事件。下面來看下 EventPublishingRunListener
類生命週期對應的事件。app
ApplicationStartingEvent
是 SpringBoot
啓動開始的時候執行的事件,在該事件中能夠獲取到 SpringApplication
對象,可作一些執行前的設置,對應的調用方法是 starting()
。異步
ApplicationEnvironmentPreparedEvent
是SpringBoot
對應 Enviroment
已經準備完畢時執行的事件,此時上下文 context
尚未建立。在該監聽中獲取到 ConfigurableEnvironment
後能夠對配置信息作操做,例如:修改默認的配置信息,增長額外的配置信息等。對應的生命週期方法是 environmentPrepared(environment)
;SpringCloud
中,引導上下文就是在這時初始化的。
當 SpringApplication
啓動而且準備好 ApplicationContext
,而且在加載任何 bean
定義以前調用了 ApplicationContextInitializers
時發佈的事件。對應的生命週期方法是contextPrepared()
ApplicationPreparedEvent
是SpringBoot
上下文 context
建立完成是發佈的事件;但此時 spring
中的 bean
尚未徹底加載完成。這裏能夠將上下文傳遞出去作一些額外的操做。可是在該監聽器中是沒法獲取自定義 bean
並進行操做的。對應的生命週期方法是 contextLoaded()
。
這個事件是在 2.0 版本才引入的;具體發佈是在應用程序上下文刷新以後,調用任何 ApplicationRunner
和 CommandLineRunner
運行程序以前。
這個和 ApplicationStartedEvent
很相似,也是在應用程序上下文刷新以後以後調用,區別在於此時ApplicationRunner
和 CommandLineRunner
已經完成調用了,也意味着 SpringBoot
加載已經完成。
SpringBoot
啓動異常時執行的事件,在異常發生時,最好是添加虛擬機對應的鉤子進行資源的回收與釋放,能友善的處理異常信息。
下面的各個事件對應的demo及打印出來的執行順序。
public class GlmapperApplicationStartingEventListener implements ApplicationListener<ApplicationStartingEvent> {
@Override
public void onApplicationEvent(ApplicationStartingEvent applicationStartingEvent) {
System.out.println("execute ApplicationStartingEvent ...");
}
}
複製代碼
public class GlmapperApplicationEnvironmentPreparedEvent implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent applicationEnvironmentPreparedEvent) {
System.out.println("execute ApplicationEnvironmentPreparedEvent ...");
}
}
複製代碼
public class GlmapperApplicationContextInitializedEvent implements ApplicationListener<ApplicationContextInitializedEvent> {
@Override
public void onApplicationEvent(ApplicationContextInitializedEvent applicationContextInitializedEvent) {
System.out.println("execute applicationContextInitializedEvent ...");
}
}
複製代碼
public class GlmapperApplicationPreparedEvent implements ApplicationListener<ApplicationPreparedEvent> {
@Override
public void onApplicationEvent(ApplicationPreparedEvent applicationPreparedEvent) {
System.out.println("execute ApplicationPreparedEvent ...");
}
}
複製代碼
public class GlmapperApplicationStartedEvent implements ApplicationListener<ApplicationStartedEvent> {
@Override
public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
System.out.println("execute ApplicationStartedEvent ...");
}
}
複製代碼
public class GlmapperApplicationReadyEvent implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
System.out.println("execute ApplicationReadyEvent ...");
}
}
複製代碼
這裏圍繞 SpringApplicationRunListener
這個類來講。在實現類 EventPublishingRunListener
中,事件發佈有兩種模式:
SimpleApplicationEventMulticaster
進行事件廣播Context
因此EventPublishingRunListener
不只負責發佈事件,並且在合適的時機將 SpringApplication
所獲取的監聽器和應用上下文做關聯。
SimpleApplicationEventMulticaster
是 Spring
默認的事件廣播器。來看下它是怎麼工做的:
@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);
}
}
}
複製代碼
從上面的代碼段能夠看出,它是經過遍歷註冊的每一個監聽器,並啓動來調用每一個監聽器的 onApplicationEvent
方法。
下面再來看下 SimpleApplicationEventMulticaster
的類集成結構:
AbstractApplicationContext
下面來聊,這個類實際上就負責了事件體系的初始化工做。
事件體系的初始化對應在 SpringBoot
啓動過程的 refreshContext
這個方法;refreshContext
具體調用 AbstractApplicationContext.refresh()方法,最後調用 initApplicationEventMulticaster() 來完成事件體系的初始化,代碼以下:
用戶能夠爲容器定義一個自定義的事件廣播器,只要實現 ApplicationEventMulticaster
就能夠了,Spring
會經過 反射的機制將其註冊成容器的事件廣播器,若是沒有找到配置的外部事件廣播器,Spring
就是默認使用 SimpleApplicationEventMulticaster
做爲事件廣播器。
事件註冊是在事件體系初始化完成以後作的事情,也是在 AbstractApplicationContext.refresh()
方法中進行調用的。
listeners
;這裏包括咱們自定義的那些監聽器。DefaultListableBeanFactory
中 getBeanNamesForType
獲得自定義的 ApplicationListener
bean
進行事件註冊。事件發佈伴隨着 SpringBoot
啓動的整個生命週期。不一樣階段對應發佈不一樣的事件,上面咱們已經對各個事件進行了分析,下面就具體看下發布事件的實現:
org.springframework.context.support.AbstractApplicationContext#publishEvent
![]()
earlyApplicationEvents 中的事件是廣播器未創建的時候保存通知信息,一旦容器創建完成,之後都是直接通知。
廣播事件最終仍是經過調用 ApplicationEventMulticaster
的 multicastEvent
來實現。而 multicastEvent
也就就是事件執行的方法。
上面 SimpleApplicationEventMulticaster
小節已經初步介紹了 multicastEvent
這個方法。補充一點, 若是有可用的 taskExecutor
會使用併發的模式執行事件,可是實際上 SimpleApplicationEventMulticaster
並無提供線程池實現,默認請況下是使用同步的方式執行事件(org.springframework.core.task.SyncTaskExecutor
),因此若是須要異步配置的話,須要本身去實現線程池。
這裏回到 SpringApplication
的run
方法,看下 SpringBoot
在啓動過程當中,各個事件階段作了哪些事情。
這裏 debug
到 starting
方法,追蹤到 multicastEvent
,這裏 type
爲 ApplicationStartingEvent
;對應的事件以下:
logging.config
環境變量指定的配置或者缺省配置context.listener.classes
指定的那些事件監聽器SpringBoot
可執行jar
包配合工做的版本替換 liquibase ServiceLocator
AnsiOutputApplicationListener:根據spring.output.ansi.enabled
參數配置AnsiOutput
ConfigFileApplicationListener:EnvironmentPostProcessor
,從常見的那些約定的位置讀取配置文件,好比從如下目錄讀取application.properties
,application.yml
等配置文件:
也能夠配置成從其餘指定的位置讀取配置文件。
ClasspathLoggingApplicationListener:對環境就緒事件ApplicationEnvironmentPreparedEvent
/應用失敗事件ApplicationFailedEvent
作出響應,往日誌DEBUG
級別輸出TCCL(thread context class loader)
的 classpath
。
FileEncodingApplicationListener:若是系統文件編碼和環境變量中指定的不一樣則終止應用啓動。具體的方法是比較系統屬性file.encoding
和環境變量spring.mandatory-file-encoding
是否相等(大小寫不敏感)。
ApplicationContextInitializer
接口,其目的是將 ConditionEvaluationReport
寫入到日誌,使用DEBUG
級別輸出。程序崩潰報告會觸發一個消息輸出,建議用戶使用調試模式顯示報告。它是在應用初始化時綁定一個ConditionEvaluationReportListener
事件監聽器,而後相應的事件發生時輸出ConditionEvaluationReport
報告。ContextRefreshedEvent
。context
註冊了一個BeanFactoryPostProcessor
:CachingMetadataReaderFactoryPostProcessor
實例。handling mappings
處理這兩個貫穿了整個過程,這裏拎出來單獨解釋下:
SpringBoot
的缺省行爲。這些初始化動做也能夠叫作預初始化。能夠經過設置系統屬性spring.backgroundpreinitializer.ignore
爲true
能夠禁用該機制。該機制被禁用時,相應的初始化任務會發生在前臺線程。context.listener.classes
指定的那些監聽器。到此,SpringBoot
中的事件相關的東西就結束了。本文從SpringApplicationRunListener
這個類提及,接着介紹 SpringBoot
啓動過程的事件以及事件的生命週期。最後介紹了 SpringBoot
中的內置的這些 監聽器在啓動過程當中對應的各個階段。
新年伊始,祝你們新年快樂!