本文共 946字,閱讀大約需要 3分鐘 !
概述
說到接觸 SpringBoot 伊始,給我第一映像最深的是有兩個關鍵元素:
![SpringBoot 的兩個關鍵元素](https://i.iter01.com/images/2f53dc4c6c58d1db7b2634ce68694763e8beeb9a342bec4aa6446ecc0a5684db.png)
對照上面的典型程式碼,這個兩個元素分別是:
- @SpringBootApplication
- SpringApplication 以及 run() 方法
關於 @SpringBootApplication 註解的剖析已經在上文:《SpringBoot 中 @SpringBootApplication註解背後的三體結構探祕》 中完成了,其實它背後就是一個三體結構,只是 SpringBoot給了其一個包裝而已。那麼本文我們就來看看這個 SpringApplication 以及 run() 方法 到底是個什麼鬼,它背後又隱藏了哪些奧祕呢?
注: 本文首發於 My Personal Blog,歡迎光臨 小站
本文內容腦圖如下:
![本文內容腦圖](https://i.iter01.com/images/408470f1ba6d506223ddeb858ba1f7f4009dd66f41cc8899edc8cdc93bc1a968.png)
SpringApplication 驚鴻一瞥
SpringApplication 這個類應該算是 SpringBoot 框架 的“創新”產物了,原始的 Spring中並沒有這個類,SpringApplication 裡面封裝了一套 Spring 應用的啟動流程,然而這對使用者完全透明,因此我們上手 SpringBoot 時感覺簡潔、輕量。
一般來說預設的 SpringApplication 執行流程已經可以滿足大部分需求,但是 若使用者想幹預這個過程,則可以通過 SpringApplication 在流程某些地方開啟的 擴充套件點 來完成對流程的擴充套件,典型的擴充套件方案那就是使用 set 方法。
我們來舉一個栗子,把我們天天司空見慣的 SpringBoot 應用的啟動類來拆解一下寫出來:
@SpringBootApplication
public class CodeSheepApplication {
public static void main( String[] args ) {
// SpringApplication.run( CodeSheepApplication.class args ); // 這是傳統SpringBoot應用的啟動,一行程式碼搞定,內部預設做了很多事
SpringApplication app = new SpringApplication( CodeSheepApplication.class );
app.setXXX( ... ); // 使用者自定的擴充套件在此 !!!
app.run( args );
}
}
複製程式碼
這樣一拆解後我們發現,我們也需要先構造 SpringApplication 類物件,然後呼叫該物件的 run()
方法。那麼接下來就講講 SpringApplication 的構造過程 以及其 run()
方法的流程,搞清楚了這個,那麼也就搞清楚了SpringBoot應用是如何執行起來的!
SpringApplication 例項的初始化
我們對照程式碼來看:
![SpringApplication 例項的初始化程式碼](https://i.iter01.com/images/b6ab839f14200704b881a0dd4b9e9575f9e0e5c6d4ef8a8a58bbdef3e8360dff.png)
四個關鍵的步驟已標註在圖中,分別解釋如下:
- ① 推斷應用的型別:建立的是 REACTIVE應用、SERVLET應用、NONE 三種中的某一種
![推斷應用的型別](https://i.iter01.com/images/3b482b81defe4e53b6de116439b7136f534e1f9a31cb8f9b20e555b99c871b8a.png)
- ② 使用
SpringFactoriesLoader
查詢並載入 classpath下META-INF/spring.factories
檔案中所有可用的ApplicationContextInitializer
![ApplicationContextInitializer 載入](https://i.iter01.com/images/b0760d2e5e382956f27c18cb9217626c163e808704365eb6b057b0a0d4a5e40c.png)
- ③ 使用
SpringFactoriesLoader
查詢並載入 classpath下META-INF/spring.factories
檔案中的所有可用的ApplicationListener
![ApplicationListener 載入](https://i.iter01.com/images/1a999ed22cd6202cfcfc57ddedbb6baf00c138d0e835e25b39f63e45494f9aec.png)
- ④ 推斷並設定 main方法的定義類
![推斷並設定main方法的定義類](https://i.iter01.com/images/6af2cd116af34c3bfe504322d664de9a04e82277c5f3544e4c3ae8c48f0f276b.png)
SpringApplication 的run()方法探祕
先看看程式碼長啥樣子:
![SpringApplication 的run()方法背後的奧祕](https://i.iter01.com/images/eac2f0e642300b1b85d4af0a12514763a69e3e7a95df15a5e91c14eb5545038d.png)
各個主要步驟我已經標註在上圖之中了,除此之外,我也按照自己的理解畫了一個流程圖如下所示,可以對照數字標示看一下:
![SpringBoot 應用啟動流程圖](https://i.iter01.com/images/d878602d114d65cfdbf9f68356ad6688d63e10f880b13e6a2f92764ca3a2b761.png)
我們將各步驟總結精煉如下:
-
通過
SpringFactoriesLoader
載入META-INF/spring.factories
檔案,獲取並建立SpringApplicationRunListener
物件 -
然後由
SpringApplicationRunListener
來發出 starting 訊息 -
建立引數,並配置當前 SpringBoot 應用將要使用的 Environment
-
完成之後,依然由
SpringApplicationRunListener
來發出 environmentPrepared 訊息 -
建立
ApplicationContext
-
初始化
ApplicationContext
,並設定 Environment,載入相關配置等 -
由
SpringApplicationRunListener
來發出contextPrepared
訊息,告知SpringBoot 應用使用的ApplicationContext
已準備OK -
將各種 beans 裝載入
ApplicationContext
,繼續由SpringApplicationRunListener
來發出 contextLoaded 訊息,告知 SpringBoot 應用使用的ApplicationContext
已裝填OK -
refresh ApplicationContext,完成IoC容器可用的最後一步
-
由
SpringApplicationRunListener
來發出 started 訊息 -
完成最終的程式的啟動
-
由
SpringApplicationRunListener
來發出 running 訊息,告知程式已執行起來了
至此,全流程結束!
後記
由於能力有限,若有錯誤或者不當之處,還請大家批評指正,一起學習交流!
可 長按 或 掃描 下面的 小心心 來訂閱作者公眾號 CodeSheep,獲取更多 務實、能看懂、可復現的 原創文 ↓↓↓
![CodeSheep · 程式羊](https://i.iter01.com/images/3788fe5789d6a44329bc128029d3203c0a6352bd2c964997b8468058a1cd4acc.png)