SpringBoot 應用程式啟動過程探祕

CodeSheep發表於2018-09-04

本文共 946字,閱讀大約需要 3分鐘 !


概述

說到接觸 SpringBoot 伊始,給我第一映像最深的是有兩個關鍵元素:

SpringBoot 的兩個關鍵元素

對照上面的典型程式碼,這個兩個元素分別是:

  • @SpringBootApplication
  • SpringApplication 以及 run() 方法

關於 @SpringBootApplication 註解的剖析已經在上文:《SpringBoot 中 @SpringBootApplication註解背後的三體結構探祕》 中完成了,其實它背後就是一個三體結構,只是 SpringBoot給了其一個包裝而已。那麼本文我們就來看看這個 SpringApplication 以及 run() 方法 到底是個什麼鬼,它背後又隱藏了哪些奧祕呢?

注: 本文首發於 My Personal Blog,歡迎光臨 小站

本文內容腦圖如下:

本文內容腦圖


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 例項的初始化程式碼

四個關鍵的步驟已標註在圖中,分別解釋如下:

  • 推斷應用的型別:建立的是 REACTIVE應用、SERVLET應用、NONE 三種中的某一種

推斷應用的型別

  • 使用 SpringFactoriesLoader查詢並載入 classpath下 META-INF/spring.factories檔案中所有可用的 ApplicationContextInitializer

ApplicationContextInitializer 載入

  • 使用 SpringFactoriesLoader查詢並載入 classpath下 META-INF/spring.factories檔案中的所有可用的 ApplicationListener

ApplicationListener 載入

  • 推斷並設定 main方法的定義類

推斷並設定main方法的定義類



SpringApplication 的run()方法探祕

先看看程式碼長啥樣子:

SpringApplication 的run()方法背後的奧祕

各個主要步驟我已經標註在上圖之中了,除此之外,我也按照自己的理解畫了一個流程圖如下所示,可以對照數字標示看一下:

SpringBoot 應用啟動流程圖

我們將各步驟總結精煉如下:

  1. 通過 SpringFactoriesLoader 載入 META-INF/spring.factories 檔案,獲取並建立 SpringApplicationRunListener 物件

  2. 然後由 SpringApplicationRunListener 來發出 starting 訊息

  3. 建立引數,並配置當前 SpringBoot 應用將要使用的 Environment

  4. 完成之後,依然由 SpringApplicationRunListener 來發出 environmentPrepared 訊息

  5. 建立 ApplicationContext

  6. 初始化 ApplicationContext,並設定 Environment,載入相關配置等

  7. SpringApplicationRunListener 來發出 contextPrepared 訊息,告知SpringBoot 應用使用的 ApplicationContext 已準備OK

  8. 將各種 beans 裝載入 ApplicationContext,繼續由 SpringApplicationRunListener 來發出 contextLoaded 訊息,告知 SpringBoot 應用使用的 ApplicationContext 已裝填OK

  9. refresh ApplicationContext,完成IoC容器可用的最後一步

  10. SpringApplicationRunListener 來發出 started 訊息

  11. 完成最終的程式的啟動

  12. SpringApplicationRunListener 來發出 running 訊息,告知程式已執行起來了

至此,全流程結束!



後記

由於能力有限,若有錯誤或者不當之處,還請大家批評指正,一起學習交流!



長按掃描 下面的 小心心 來訂閱作者公眾號 CodeSheep,獲取更多 務實、能看懂、可復現的 原創文 ↓↓↓

CodeSheep · 程式羊


相關文章