SpringBoot中文注釋項目Github地址:
https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE
本篇接 SpringApplication對象是如何構建的? SpringBoot源碼(八)
1 溫故而知新
溫故而知新,我們來簡單回顧一下上篇的內容,上一篇我們分析了SpringApplication對象的構建過程及SpringBoot自己實現的一套SPI機制,現將關鍵步驟再濃縮總結下:
SpringApplication
對象的構造過程其實就是給SpringApplication
類的6個成員變量賦值;- SpringBoot通過以下步驟實現自己的SPI機制:
- 1)首先獲取線程上下文類加載器;
- 2)然后利用上下文類加載器從
spring.factories
配置文件中加載所有的SPI擴展實現類並放入緩存中; - 3)根據SPI接口從緩存中取出相應的SPI擴展實現類;
- 4)實例化從緩存中取出的SPI擴展實現類並返回。
2 引言
在SpringBoot啟動過程中,每個不同的啟動階段會分別廣播不同的內置生命周期事件,然后相應的監聽器會監聽這些事件來執行一些初始化邏輯工作比如ConfigFileApplicationListener
會監聽onApplicationEnvironmentPreparedEvent
事件來加載配置文件application.properties
的環境變量等。
因此本篇內容將來分析下SpringBoot的事件監聽機制的源碼。
3 SpringBoot廣播內置生命周期事件流程分析
為了探究SpringBoot廣播內置生命周期事件流程,我們再來回顧一下SpringBoot的啟動流程代碼:
// SpringApplication.java
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 【0】新建一個SpringApplicationRunListeners對象用於發射SpringBoot啟動過程中的生命周期事件
SpringApplicationRunListeners listeners = getRunListeners(args);
// 【1】》》》》》發射【ApplicationStartingEvent】事件,標志SpringApplication開始啟動
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 【2】》》》》》發射【ApplicationEnvironmentPreparedEvent】事件,此時會去加載application.properties等配置文件的環境變量,同時也有標志環境變量已經准備好的意思
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 【3】》》》》》發射【ApplicationContextInitializedEvent】事件,標志context容器被創建且已准備好
// 【4】》》》》》發射【ApplicationPreparedEvent】事件,標志Context容器已經准備完成
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
// 【5】》》》》》發射【ApplicationStartedEvent】事件,標志spring容器已經刷新,此時所有的bean實例都已經加載完畢
listeners.started(context);
callRunners(context, applicationArguments);
}
// 【6】》》》》》發射【ApplicationFailedEvent】事件,標志SpringBoot啟動失敗
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// 【7】》》》》》發射【ApplicationReadyEvent】事件,標志SpringApplication已經正在運行即已經成功啟動,可以接收服務請求了。
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
可以看到SpringBoot在啟動過程中首先會先新建一個SpringApplicationRunListeners
對象用於發射SpringBoot啟動過程中的各種生命周期事件,比如發射ApplicationStartingEvent
,ApplicationEnvironmentPreparedEvent
和ApplicationContextInitializedEvent
等事件,然后相應的監聽器會執行一些SpringBoot啟動過程中的初始化邏輯。那么,監聽這些SpringBoot的生命周期事件的監聽器們是何時被加載實例化的呢?還記得上篇文章在分析SpringApplication
的構建過程嗎?沒錯,這些執行初始化邏輯的監聽器們正是在SpringApplication
的構建過程中根據ApplicationListener
接口去spring.factories
配置文件中加載並實例化的。
3.1 為廣播SpringBoot內置生命周期事件做前期准備
3.1.1 加載ApplicationListener監聽器實現類
我們再來回顧下SpringApplication對象是如何構建的? SpringBoot源碼(八)一文中講到在構建SpringApplication
對象時的setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
這句代碼。
這句代碼做的事情就是從spring.factories
中加載出ApplicationListener
事件監聽接口的SPI擴展實現類然后添加到SpringApplication
對象的listeners
集合中,用於后續監聽SpringBoot啟動過程中的事件,來執行一些初始化邏輯工作。
SpringBoot啟動時的具體監聽器們都實現了ApplicationListener
接口,其在spring.factories
部分配置如下:
不過在調試時,會從所有的spring.factories配置文件中加載監聽器,最終加載了10個監聽器。如下圖:
3.1.2 加載SPI擴展類EventPublishingRunListener
前面講到,在SpringBoot的啟動過程中首先會先新建一個SpringApplicationRunListeners
對象用於發射SpringBoot啟動過程中的生命周期事件,即我們現在來看下SpringApplicationRunListeners listeners = getRunListeners(args);
這句代碼:
// SpringApplication.java
private SpringApplicationRunListeners getRunListeners(String[] args) {
// 構造一個由SpringApplication.class和String[].class組成的types
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
// 1) 根據SpringApplicationRunListener接口去spring.factories配置文件中加載其SPI擴展實現類
// 2) 構建一個SpringApplicationRunListeners對象並返回
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
我們將重點放到getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)
這句代碼,getSpringFactoriesInstances
這個方法我們已經很熟悉,在上一篇分析SpringBoot的SPI機制時已經詳細分析過這個方法。可以看到SpringBoot此時又是根據SpringApplicationRunListener
這個SPI接口去spring.factories
中加載相應的SPI擴展實現類,我們直接去spring.factories
中看看SpringApplicationRunListener
有哪些SPI實現類:
由上圖可以看到,SpringApplicationRunListener
只有EventPublishingRunListener
這個SPI實現類
EventPublishingRunListener
這個哥們在SpringBoot的啟動過程中尤其重要,由其在SpringBoot啟動過程的不同階段發射不同的SpringBoot的生命周期事件,即SpringApplicationRunListeners
對象沒有承擔廣播事件的職責,而最終是委托EventPublishingRunListener
這個哥們來廣播事件的。
因為從spring.factories
中加載EventPublishingRunListener
類后還會實例化該類,那么我們再跟進EventPublishingRunListener
的源碼,看看其是如何承擔發射SpringBoot生命周期事件這一職責的?
// EventPublishingRunListener.java
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
private final SpringApplication application;
private final String[] args;
/**
* 擁有一個SimpleApplicationEventMulticaster事件廣播器來廣播事件
*/
private final SimpleApplicationEventMulticaster initialMulticaster;
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
// 新建一個事件廣播器SimpleApplicationEventMulticaster對象
this.initialMulticaster = new SimpleApplicationEventMulticaster();
// 遍歷在構造SpringApplication對象時從spring.factories配置文件中獲取的事件監聽器
for (ApplicationListener<?> listener : application.getListeners()) {
// 將從spring.factories配置文件中獲取的事件監聽器們添加到事件廣播器initialMulticaster對象的相關集合中
this.initialMulticaster.addApplicationListener(listener);
}
}
@Override
public int getOrder() {
return 0;
}
// 》》》》》發射【ApplicationStartingEvent】事件
@Override
public void starting() {
this.initialMulticaster.multicastEvent(
new ApplicationStartingEvent(this.application, this.args));
}
// 》》》》》發射【ApplicationEnvironmentPreparedEvent】事件
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
this.application, this.args, environment));
}
// 》》》》》發射【ApplicationContextInitializedEvent】事件
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(
this.application, this.args, context));
}
// 》》》》》發射【ApplicationPreparedEvent】事件
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener<?> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware) {
((ApplicationContextAware) listener).setApplicationContext(context);
}
context.addApplicationListener(listener);
}
this.initialMulticaster.multicastEvent(
new ApplicationPreparedEvent(this.application, this.args, context));
}
// 》》》》》發射【ApplicationStartedEvent】事件
@Override
public void started(ConfigurableApplicationContext context) {
context.publishEvent(
new ApplicationStartedEvent(this.application, this.args, context));
}
// 》》》》》發射【ApplicationReadyEvent】事件
@Override
public void running(ConfigurableApplicationContext context) {
context.publishEvent(
new ApplicationReadyEvent(this.application, this.args, context));
}
// 》》》》》發射【ApplicationFailedEvent】事件
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
ApplicationFailedEvent event = new ApplicationFailedEvent(this.application,
this.args, context, exception);
if (context != null && context.isActive()) {
// Listeners have been registered to the application context so we should
// use it at this point if we can
context.publishEvent(event);
}
else {
// An inactive context may not have a multicaster so we use our multicaster to
// call all of the context's listeners instead
if (context instanceof AbstractApplicationContext) {
for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
.getApplicationListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
this.initialMulticaster.multicastEvent(event);
}
}
// ...省略非關鍵代碼
}
可以看到EventPublishingRunListener
類實現了SpringApplicationRunListener
接口,SpringApplicationRunListener
接口定義了SpringBoot啟動時發射生命周期事件的接口方法,而EventPublishingRunListener
類正是通過實現SpringApplicationRunListener
接口的starting
,environmentPrepared
和contextPrepared
等方法來廣播SpringBoot不同的生命周期事件,我們直接看下SpringApplicationRunListener
接口源碼好了:
// SpringApplicationRunListener.java
public interface SpringApplicationRunListener {
void starting();
void environmentPrepared(ConfigurableEnvironment environment);
void contextPrepared(ConfigurableApplicationContext context);
void contextLoaded(ConfigurableApplicationContext context);
void started(ConfigurableApplicationContext context);
void running(ConfigurableApplicationContext context);
void failed(ConfigurableApplicationContext context, Throwable exception);
}
我們再接着分析EventPublishingRunListener
這個類,可以看到其有一個重要的成員屬性initialMulticaster
,該成員屬性是SimpleApplicationEventMulticaster
類對象,該類正是承擔了廣播SpringBoot啟動時生命周期事件的職責,即EventPublishingRunListener
對象沒有承擔廣播事件的職責,而最終是委托SimpleApplicationEventMulticaster
這個哥們來廣播事件的。 從EventPublishingRunListener
的源碼中也可以看到在starting
,environmentPrepared
和contextPrepared
等方法中也正是通過調用SimpleApplicationEventMulticaster
類對象的multicastEvent
方法來廣播事件的。
思考 SpringBoot啟動過程中發射事件時事件廣播者是層層委托職責的,起初由
SpringApplicationRunListeners
對象承擔,然后SpringApplicationRunListeners
對象將廣播事件職責委托給EventPublishingRunListener
對象,最終EventPublishingRunListener
對象將廣播事件的職責委托給SimpleApplicationEventMulticaster
對象。為什么要層層委托這么做呢? 這個值得大家思考。
前面講到從spring.factories
中加載出EventPublishingRunListener
類后會實例化,而實例化必然會通過EventPublishingRunListener
的構造函數來進行實例化,因此我們接下來分析下EventPublishingRunListener
的構造函數源碼:
// EventPublishingRunListener.java
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
// 新建一個事件廣播器SimpleApplicationEventMulticaster對象
this.initialMulticaster = new SimpleApplicationEventMulticaster();
// 遍歷在構造SpringApplication對象時從spring.factories配置文件中獲取的事件監聽器
for (ApplicationListener<?> listener : application.getListeners()) {
// 將從spring.factories配置文件中獲取的事件監聽器們添加到事件廣播器initialMulticaster對象的相關集合中
this.initialMulticaster.addApplicationListener(listener);
}
}
可以看到在EventPublishingRunListener
的構造函數中有一個for
循環會遍歷之前從spring.factories
中加載的監聽器們,然后添加到集合中緩存起來,用於以后廣播各種事件時直接從這個集合中取出來即可,而不用再去spring.factories
中加載,提高效率。
3.2 廣播SpringBoot的內置生命周期事件
從spring.factories
配置文件中加載並實例化EventPublishingRunListener
對象后,那么在在SpringBoot的啟動過程中會發射一系列SpringBoot內置的生命周期事件,我們再來回顧下SpringBoot啟動過程中的源碼:
// SpringApplication.java
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 【0】新建一個SpringApplicationRunListeners對象用於發射SpringBoot啟動過程中的生命周期事件
SpringApplicationRunListeners listeners = getRunListeners(args);
// 【1】》》》》》發射【ApplicationStartingEvent】事件,標志SpringApplication開始啟動
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 【2】》》》》》發射【ApplicationEnvironmentPreparedEvent】事件,此時會去加載application.properties等配置文件的環境變量,同時也有標志環境變量已經准備好的意思
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 【3】》》》》》發射【ApplicationContextInitializedEvent】事件,標志context容器被創建且已准備好
// 【4】》》》》》發射【ApplicationPreparedEvent】事件,標志Context容器已經准備完成
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
// 【5】》》》》》發射【ApplicationStartedEvent】事件,標志spring容器已經刷新,此時所有的bean實例都已經加載完畢
listeners.started(context);
callRunners(context, applicationArguments);
}
// 【6】》》》》》發射【ApplicationFailedEvent】事件,標志SpringBoot啟動失敗
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// 【7】》》》》》發射【ApplicationReadyEvent】事件,標志SpringApplication已經正在運行即已經成功啟動,可以接收服務請求了。
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
可以看到在SpringBoot的啟動過程中總共會發射7種不同類型的生命周期事件,來標志SpringBoot的不同啟動階段,同時,這些生命周期事件的監聽器們也會執行一些啟動過程中的初始化邏輯,關於這些監聽器的初始化邏輯將在下一篇內容中會分析。以下是SpringBoot啟動過程中要發射的事件類型,其中ApplicationFailedEvent
在SpringBoot啟動過程中遇到異常才會發射:
ApplicationStartingEvent
ApplicationEnvironmentPreparedEvent
ApplicationContextInitializedEvent
ApplicationPreparedEvent
ApplicationStartedEvent
ApplicationFailedEvent
ApplicationReadyEvent
我們以listeners.starting();
這句代碼為例,看看EventPublishingRunListener
對象發射事件的源碼:
// SpringApplicationRunListeners.java
public void starting() {
// 遍歷listeners集合,這里實質取出的就是剛才從spring.factories中取出的SPI實現類EventPublishingRunListener
// 而EventPublishingRunListener對象承擔了SpringBoot啟動過程中負責廣播不同的生命周期事件
for (SpringApplicationRunListener listener : this.listeners) {
// 調用EventPublishingRunListener的starting方法來廣播ApplicationStartingEvent事件
listener.starting();
}
}
繼續跟進listener.starting();
的源碼:
EventPublishingRunListener.java
// 》》》》》發射【ApplicationStartingEvent】事件
public void starting() {
// EventPublishingRunListener對象將發布ApplicationStartingEvent這件事情委托給了initialMulticaster對象
// 調用initialMulticaster的multicastEvent方法來發射ApplicationStartingEvent事件
this.initialMulticaster.multicastEvent(
new ApplicationStartingEvent(this.application, this.args));
}
可以看到,EventPublishingRunListener
對象將發布ApplicationStartingEvent
這件事情委托給了SimpleApplicationEventMulticaster
對象initialMulticaster
,
,而initialMulticaster
對象最終會調用其multicastEvent
方法來發射ApplicationStartingEvent
事件。關於SimpleApplicationEventMulticaster
類如何廣播事件,筆者已經在Spring是如何實現事件監聽機制的? Spring源碼(二)這篇文章已經詳細分析,這里不再贅述。
關於SpringBoot啟動過程中發射其他生命周期事件的源碼這里不再分析
4 SpringBoot的內置生命周期事件總結
好了,前面已經分析了SpringBoot啟動過程中要發射的各種生命周期事件,下面列一個表格總結下:
5 小結
SpringBoot啟動過程中廣播生命周期事件的源碼分析就到此結束了,下一篇會繼續介紹監聽這些生命周期事件的監聽器們。我們再回顧本篇內容總結下關鍵點:
SpringBoot啟動過程中會發射7種類型的生命周期事件,標志不同的啟動階段,然后相應的監聽器會監聽這些事件來執行一些初始化邏輯工作;
【源碼筆記】Github源碼分析項目上線啦!!!下面是筆記的Github地址:
https://github.com/yuanmabiji/Java-SourceCode-Blogs
點贊和轉發是對筆者最大的激勵哦!
公眾號【源碼筆記】,專注於Java后端系列框架的源碼分析。