SpringBoot-SpringBoot中的事件機制

微信公衆號:glmapper工做室
掘金專欄:glmapper
微 博:瘋狂的石頭_henu
歡迎關注,一塊兒學習分享技術java

在這篇文章中聊一聊 Spring 中的擴展機制(一)中對Spring中的事件機制進行了分析。那麼對於 SpringBoot 來講,它在 Spring 的基礎上又作了哪些拓展呢?本篇未來聊一聊 SpringBoot 中的事件。git

在 SpringBoot 的啓動過程當中,會經過 SPI 機制去加載 spring.factories 下面的一些類,這裏面就包括了事件相關的類。github

  • SpringApplicationRunListener
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
複製代碼
  • ApplicationListener
# 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。另外還有一個新增的類是SpringApplicationRunListenersSpringApplicationRunListeners 中包含了多個 SpringApplicationRunListenerspring

SpringApplicationRunListener

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

上面提到,SpringApplicationRunListenersSpringApplicationRunListener的集合,裏面包括了不少SpringApplicationRunListener實例;SpringApplication 類實際上使用的是 SpringApplicationRunListeners 類,與 SpringApplicationRunListener 生命週期相同,調用各個週期的 SpringApplicationRunListener 。而後廣播相應的事件到 ApplicationListener微信

代碼詳見:SpringApplicationRunListeners.併發

EventPublishingRunListener

EventPublishingRunListener 類是 SpringApplicationRunListener接口的實現類 ,它具備廣播事件的功能。其內部使用 ApplicationEventMulticaster在實際刷新上下文以前發佈事件。下面來看下 EventPublishingRunListener 類生命週期對應的事件。app

ApplicationStartingEvent

ApplicationStartingEventSpringBoot 啓動開始的時候執行的事件,在該事件中能夠獲取到 SpringApplication 對象,可作一些執行前的設置,對應的調用方法是 starting()異步

ApplicationEnvironmentPreparedEvent

ApplicationEnvironmentPreparedEventSpringBoot 對應 Enviroment 已經準備完畢時執行的事件,此時上下文 context 尚未建立。在該監聽中獲取到 ConfigurableEnvironment 後能夠對配置信息作操做,例如:修改默認的配置信息,增長額外的配置信息等。對應的生命週期方法是 environmentPrepared(environment)SpringCloud 中,引導上下文就是在這時初始化的。

ApplicationContextInitializedEvent

SpringApplication 啓動而且準備好 ApplicationContext,而且在加載任何 bean 定義以前調用了 ApplicationContextInitializers 時發佈的事件。對應的生命週期方法是contextPrepared()

ApplicationPreparedEvent

ApplicationPreparedEventSpringBoot上下文 context 建立完成是發佈的事件;但此時 spring 中的 bean 尚未徹底加載完成。這裏能夠將上下文傳遞出去作一些額外的操做。可是在該監聽器中是沒法獲取自定義 bean 並進行操做的。對應的生命週期方法是 contextLoaded()

ApplicationStartedEvent

這個事件是在 2.0 版本才引入的;具體發佈是在應用程序上下文刷新以後,調用任何 ApplicationRunnerCommandLineRunner 運行程序以前。

ApplicationReadyEvent

這個和 ApplicationStartedEvent 很相似,也是在應用程序上下文刷新以後以後調用,區別在於此時ApplicationRunnerCommandLineRunner已經完成調用了,也意味着 SpringBoot 加載已經完成。

ApplicationFailedEvent

SpringBoot 啓動異常時執行的事件,在異常發生時,最好是添加虛擬機對應的鉤子進行資源的回收與釋放,能友善的處理異常信息。

demo 及各個事件的執行順序

下面的各個事件對應的demo及打印出來的執行順序。

  • GlmapperApplicationStartingEventListener
public class GlmapperApplicationStartingEventListener implements ApplicationListener<ApplicationStartingEvent> {
    @Override
    public void onApplicationEvent(ApplicationStartingEvent applicationStartingEvent) {
        System.out.println("execute ApplicationStartingEvent ...");
    }
}
複製代碼
  • GlmapperApplicationEnvironmentPreparedEvent
public class GlmapperApplicationEnvironmentPreparedEvent implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent applicationEnvironmentPreparedEvent) {
        System.out.println("execute ApplicationEnvironmentPreparedEvent ...");
    }
}
複製代碼
  • GlmapperApplicationContextInitializedEvent
public class GlmapperApplicationContextInitializedEvent implements ApplicationListener<ApplicationContextInitializedEvent> {
    @Override
    public void onApplicationEvent(ApplicationContextInitializedEvent applicationContextInitializedEvent) {
        System.out.println("execute applicationContextInitializedEvent ...");
    }
}
複製代碼
  • GlmapperApplicationPreparedEvent
public class GlmapperApplicationPreparedEvent implements ApplicationListener<ApplicationPreparedEvent> {
    @Override
    public void onApplicationEvent(ApplicationPreparedEvent applicationPreparedEvent) {
        System.out.println("execute ApplicationPreparedEvent ...");
    }
}
複製代碼
  • GlmapperApplicationStartedEvent
public class GlmapperApplicationStartedEvent implements ApplicationListener<ApplicationStartedEvent> {
    @Override
    public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
        System.out.println("execute ApplicationStartedEvent ...");
    }
}
複製代碼
  • GlmapperApplicationReadyEvent
public class GlmapperApplicationReadyEvent implements ApplicationListener<ApplicationReadyEvent> {
    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        System.out.println("execute ApplicationReadyEvent ...");
    }
}
複製代碼
  • 執行結果

SpringBoot 中的事件體系

這裏圍繞 SpringApplicationRunListener 這個類來講。在實現類 EventPublishingRunListener 中,事件發佈有兩種模式:

  • 經過 SimpleApplicationEventMulticaster 進行事件廣播
  • 全部監聽器交給相應的 Context

因此EventPublishingRunListener 不只負責發佈事件,並且在合適的時機將 SpringApplication 所獲取的監聽器和應用上下文做關聯。

SimpleApplicationEventMulticaster

SimpleApplicationEventMulticasterSpring 默認的事件廣播器。來看下它是怎麼工做的:

@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;這裏包括咱們自定義的那些監聽器。
  • 調用 DefaultListableBeanFactorygetBeanNamesForType 獲得自定義的 ApplicationListener bean 進行事件註冊。
  • 廣播早期的事件。

事件廣播

事件發佈伴隨着 SpringBoot 啓動的整個生命週期。不一樣階段對應發佈不一樣的事件,上面咱們已經對各個事件進行了分析,下面就具體看下發布事件的實現:

org.springframework.context.support.AbstractApplicationContext#publishEvent

earlyApplicationEvents 中的事件是廣播器未創建的時候保存通知信息,一旦容器創建完成,之後都是直接通知。

廣播事件最終仍是經過調用 ApplicationEventMulticastermulticastEvent 來實現。而 multicastEvent 也就就是事件執行的方法。

事件執行

上面 SimpleApplicationEventMulticaster 小節已經初步介紹了 multicastEvent 這個方法。補充一點, 若是有可用的 taskExecutor 會使用併發的模式執行事件,可是實際上 SimpleApplicationEventMulticaster 並無提供線程池實現,默認請況下是使用同步的方式執行事件(org.springframework.core.task.SyncTaskExecutor),因此若是須要異步配置的話,須要本身去實現線程池。

SpringBoot 啓動過程當中的事件階段

這裏回到 SpringApplicationrun方法,看下 SpringBoot 在啓動過程當中,各個事件階段作了哪些事情。

starting -> ApplicationStartingEvent

這裏 debugstarting 方法,追蹤到 multicastEvent,這裏 typeApplicationStartingEvent;對應的事件以下:

  • LoggerApplicationListener:配置日誌系統。使用logging.config環境變量指定的配置或者缺省配置
  • BackgroundPreinitializer:儘早觸發一些耗時的初始化任務,使用一個後臺線程
  • DelegatingApplicationListener:監聽到事件後轉發給環境變量context.listener.classes指定的那些事件監聽器
  • LiquibaseServiceLocatorApplicationListener:使用一個能夠和 SpringBoot 可執行jar包配合工做的版本替換 liquibase ServiceLocator

listeners.environmentPrepared->ApplicationEnvironmentPreparedEvent

  • AnsiOutputApplicationListener:根據spring.output.ansi.enabled參數配置AnsiOutput

  • ConfigFileApplicationListener:EnvironmentPostProcessor,從常見的那些約定的位置讀取配置文件,好比從如下目錄讀取application.properties,application.yml等配置文件:

    • classpath:
    • file:.
    • classpath:config
    • file:./config/

    也能夠配置成從其餘指定的位置讀取配置文件。

  • ClasspathLoggingApplicationListener:對環境就緒事件ApplicationEnvironmentPreparedEvent/應用失敗事件ApplicationFailedEvent作出響應,往日誌DEBUG級別輸出TCCL(thread context class loader)classpath

  • FileEncodingApplicationListener:若是系統文件編碼和環境變量中指定的不一樣則終止應用啓動。具體的方法是比較系統屬性file.encoding和環境變量spring.mandatory-file-encoding是否相等(大小寫不敏感)。

listeners.contextPrepared->ApplicationContextInitializedEvent

相關監聽器參考上面的描述。

listeners.contextLoaded->ApplicationPreparedEvent

相關監聽器參考上面的描述。

refresh->ContextRefreshedEvent

  • ConditionEvaluationReportLoggingListener:實際上實現的是 ApplicationContextInitializer接口,其目的是將 ConditionEvaluationReport 寫入到日誌,使用DEBUG級別輸出。程序崩潰報告會觸發一個消息輸出,建議用戶使用調試模式顯示報告。它是在應用初始化時綁定一個ConditionEvaluationReportListener事件監聽器,而後相應的事件發生時輸出ConditionEvaluationReport報告。
  • ClearCachesApplicationListener:應用上下文加載完成後對緩存作清除工做,響應事件ContextRefreshedEvent
  • SharedMetadataReaderFactoryContextInitializer: 向context註冊了一個BeanFactoryPostProcessorCachingMetadataReaderFactoryPostProcessor實例。
  • ResourceUrlProvider:handling mappings處理

started->ApplicationStartedEvent

相關監聽器參考上面的描述。

running->ApplicationReadyEvent

相關監聽器參考上面的描述。

BackgroundPreinitializer&DelegatingApplicationListener

這兩個貫穿了整個過程,這裏拎出來單獨解釋下:

  • BackgroundPreinitializer:對於一些耗時的任務使用一個後臺線程儘早觸發它們開始執行初始化,這是SpringBoot的缺省行爲。這些初始化動做也能夠叫作預初始化。能夠經過設置系統屬性spring.backgroundpreinitializer.ignoretrue能夠禁用該機制。該機制被禁用時,相應的初始化任務會發生在前臺線程。
  • DelegatingApplicationListener:監聽應用事件,並將這些應用事件廣播給環境屬性context.listener.classes指定的那些監聽器。

小結

到此,SpringBoot 中的事件相關的東西就結束了。本文從SpringApplicationRunListener這個類提及,接着介紹 SpringBoot 啓動過程的事件以及事件的生命週期。最後介紹了 SpringBoot中的內置的這些 監聽器在啓動過程當中對應的各個階段。

新年伊始,祝你們新年快樂!

參考

相關文章
相關標籤/搜索