SpringBoot中文注釋項目Github位址: https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE 本篇接 SpringApplication對象是如何建構的? SpringBoot源碼(八)
1 溫故而知新
溫故而知新,我們來簡單回顧一下上篇的内容,上一篇我們分析了SpringApplication對象的建構過程及SpringBoot自己實作的一套SPI機制,現将關鍵步驟再濃縮總結下:
-
對象的構造過程其實就是給SpringApplication
類的6個成員變量指派;SpringApplication
- SpringBoot通過以下步驟實作自己的SPI機制:
- 1)首先擷取線程上下文類加載器;
- 2)然後利用上下文類加載器從
配置檔案中加載所有的SPI擴充實作類并放入緩存中;spring.factories
- 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
對象時的
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點贊和轉發是對筆者最大的激勵哦!