SpringBoot修行之路-3.探祕SpringApplication

林淺時無人發表於2020-11-16

概述

一個標準的SpringBoot應用都具有如下類似程式碼:
在這裡插入圖片描述
這是我自己搭建的一個專案。裡面只有一個註解@SpringBootApplication。這個註解有倆個作用:

  1. 標明這是一個SpringBoot應用
  2. 開啟自動配置的功能

最後通過SpringApplication的run方法啟動應用。這是我們使用SpringBoot應用的的時候最常用的程式碼。

SpringApplication

執行專案中的啟動類的run方法後,就到了org.springframework.boot.SpringApplication的run方法。程式碼如下:
在這裡插入圖片描述
之後再繼續呼叫run方法。如下:
在這裡插入圖片描述
這裡建立了一個SpringApplication物件,然後執行run方法。關於SpringApplication物件屬性簡介,程式碼如下:
在這裡插入圖片描述
一些簡單的註釋我就直接放在截圖裡了,此處不再過多敘述。(ps:這個是我打斷點的時候截的圖)
自身的構造方法如下:
在這裡插入圖片描述
構造方法中初始化了一些屬性。webApplicationType的屬性具體實現是通過判斷是否存在指定的類來賦值。程式碼如下:
在這裡插入圖片描述

在這裡插入圖片描述
屬性mainApplicationClass是通過呼叫方法,判斷是哪個呼叫了main方法來推斷並設定專案main()方法啟動的主程式啟動類。程式碼如下:

在這裡插入圖片描述
簡單的看完了以後,我們關注的重點是Initializers和Listeners 屬性,倆者都是通過getSpringFactoriesInstances()方法獲得不同型別的陣列。
啟動原始碼的測試案例就可以看到這倆個的值。Initializers屬性的值如下:
在這裡插入圖片描述
Listenners屬性在SpringBoot原始碼的測試用例中如下圖:
在這裡插入圖片描述
這裡簡單說一下getSpringFactoriesInstances()方法。具體如下:在這裡插入圖片描述

主要是通過Sping.factories的配置檔案來獲取對應的值。這裡names的值如下:org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

spring.factories部分內容截圖如下:
在這裡插入圖片描述
建立物件createSpringFactoriesInstances()方法的程式碼具體實現如下:
在這裡插入圖片描述
排序方法就不在多說,此處是實現了Conparator進行排序。
那麼此處例項化Application做了哪些事情呢?

  1. 將專案啟動類的class設定為屬性儲存起來
  2. 設定應用型別
  3. 設定初始化器
  4. 設定監聽器(初始化器和監聽器最後在run方法中都會呼叫)
  5. 設定mainApplicationClass屬性

建立完SpringApplication物件後,接下來看run方法。run方法整體邏輯較長。程式碼如下圖:
在這裡插入圖片描述
從上述程式碼中可以看出,專案初始化啟動大致包括以下幾個部分:

  1. 獲取並啟動監聽器
    getRunListeners(args)和listeners.starting()方法主要用於獲取SpringApplication例項化過程中初始化的SpringApplicationRunListener監聽器,並執行。getRunListeners(args)的程式碼具體實現如下:
    在這裡插入圖片描述
    也是通過getSpringFactoriesInstances()方法來獲取一個SpringApplicationRunListener型別的監聽器。

  2. 根據SpringApplicationRunListeners以及引數來準備環境
    prepareEnvironment(listeners,applicationArguments)方法主要用於對專案執行環境進行預設定,程式碼如下:
    在這裡插入圖片描述
    1. 根據不同的webApplicationType建立環境
    2. 載入指定的配置檔案(包括多環境配置)
    3. 通知監聽器,環境變數已經載入完成
    4. 最後將環境繫結到SpringApplication
    基本思路就是:建立環境,設定引數,通知監聽器,繫結到SpringApplication
    然後,通過configureIgnoreBeanInfo(environment)方法排除一些不需要的執行環境

  3. 建立Spring容器
    根據webApplicationType判斷,確定容器型別,如果是Servlet型別,則通過反射裝載對應的位元組碼,也就是AnnotationConfigServletWebServerApplication.程式碼如下:
    在這裡插入圖片描述

  4. Spring容器的前置處理
    主要是在容器重新整理前的操作,比如設定容器環境,包括各種變數。最關鍵的操作是:將啟動類注入容器,為後續開啟自動配置奠定基礎。具體實現如下:
    在這裡插入圖片描述
    主要是對上下文進行一個預處理,比如監聽器的響應事件,載入資源,設定上下文環境等等。

  5. 重新整理容器
    開啟重新整理Spring容器,通過refresh方法對整個IOC容器的初始化(包括bean資源的丁文,解析,註冊等),同時向JVM執行時註冊一個關機鉤子函式,在JVM關機時候能夠關閉上下文,除非已經關閉。具體實現如下:
    在這裡插入圖片描述
    注:第四步和第五步就是用之前的context初始化設定的context(應用上下文環境),environment(專案執行環境),listeners(監聽器),applicationArguments(專案引數),和printedBanner(專案圖示資訊)進行應用上下文的組裝配置,並重新整理配置。

  6. Spring容器的後置處理
    擴充介面,理由設計模式的模板方法,預設為空實現。如果有自定義需要,可以重新實現該方法。

  7. 發出結束執行的事件
    獲取EventPublishingRunListener監聽器,並執行started方法,並且將建立的Spring容器傳進去,建立一個ApplicationStartedEvent事件,然後執行ConfigurableApplicationContext的publishEvent方法。也就是說在這裡是在Spring容器中釋出時間,前面的starting是直接向SpringApplication中的監聽器釋出啟動事件。

  8. 執行Runners
    用於呼叫專案中自定義的執行器xxxRunners類,使得在專案啟動完成後立即執行一些特點的程式。其中SpringBoot提供倆種執行器介面,分別是ApplicationRunner和CommandLineRunner。在使用時,只需要自定義一個執行器類實現其中的一個介面並重寫對應的run方法介面,專案啟動後悔立即執行這些特定的程式。實現如下:
    在這裡插入圖片描述
    總結一下,run方法的執行過程中,發生了什麼事情。
    第一步:獲取並啟動監聽器

    主要通過getSpringFactoriesInstances()方法來獲取一個SpringApplicationRunListeners型別的監聽器

    第二步:根據SpringApplicationRunListeners以及引數來準備環境:

    1.根據不同的webApplicationType建立環境
    2.載入配置檔案
    3.通知監聽器,環境變數準備完成
    4.將環境繫結到SpringApplication
    5.排除不需要的執行環境條件

    第三步:建立Spring容器。

    通過webApplicationType的不同型別建立ApplicationContext

    第四步:Spring容器的前置處理

    主要是對上下文進行一個預處理,比如監聽器的響應時間,載入資源,設定上下文環境等等

    第五步:重新整理容器

    開啟重新整理容器,對整個IOC容器進行初始化,並註冊關機鉤子函式

    第六步:後置處理

    空實現。使用設計模式的模板方法,可以自定義實現

    第七步:發出結束執行的事件

    這裡主要是在Spring容器中釋出事件,和前面的starting方法不一樣,前面的是直接向SpringApplication中的監聽器釋出事件。

    第八步:執行Runners

    用於呼叫自定義的執行器,預設倆種執行器介面ApplicationRunner和CommandLineRunner。

相關文章