前面給大家介紹了SpringBoot啟動的核心流程,本文開始給大家詳細的來介紹SpringBoot啟動中的具體實現的相關細節。

SpringBoot2.png
SpringApplication構造器
首先我們來看下在SpringApplication的構造方法中是如何幫我們完成這4個核心操作的。

image.png
1 @SuppressWarnings({ "unchecked", "rawtypes" })
2 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
3 // 傳遞的resourceLoader為null
4 this.resourceLoader = resourceLoader;
5 Assert.notNull(primarySources, "PrimarySources must not be null");
6 // 記錄主方法的配置類名稱
7 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
8 // 記錄當前項目的類型
9 this.webApplicationType = WebApplicationType.deduceFromClasspath();
10 // 加載配置在spring.factories文件中的ApplicationContextInitializer對應的類型並實例化
11 // 並將加載的數據存儲在了 initializers 成員變量中。
12 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
13 // 初始化監聽器 並將加載的監聽器實例對象存儲在了listeners成員變量中
14 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
15 // 反推main方法所在的Class對象 並記錄在了mainApplicationClass對象中
16 this.mainApplicationClass = deduceMainApplicationClass();
17 }
1.webApplicationType
首先來看下webApplicationType是如何來推導出當前啟動的項目的類型。通過代碼可以看到是通過deduceFromClassPath()方法根據ClassPath來推導出來的。
1this.webApplicationType = WebApplicationType.deduceFromClasspath();
跟蹤進去看代碼

在看整體的實現邏輯之前,我們先分別看兩個內容,第一就是在上面的代碼中使用到了相關的靜態變量。

image.png
這些靜態變量其實就是一些綁定的Java類的全類路徑。第二個就是 ClassUtils.isPresent()方法,該方法的邏輯也非常簡單,就是通過反射的方式獲取對應的類型的Class對象,如果存在返回true,否則返回false

image.png
所以到此推導的邏輯就非常清楚了

image.png
2.setInitializers
然後我們再來看下如何實現加載初始化器的。
1// 加載配置在spring.factories文件中的ApplicationContextInitializer對應的類型並實例化
2 // 並將加載的數據存儲在了 initializers 成員變量中。
3 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
首先所有的初始化器都實現了
ApplicationContextInitializer接口,也就是根據這個類型來加載相關的實現類。

然後加載的關鍵方法是
getSpringFactoriesInstances()方法。該方法會加載 spring.factories文件中的key為
org.springframework.context.ApplicationContextInitializer 的值。
spring-boot項目下
1# Application Context Initializers
2org.springframework.context.ApplicationContextInitializer=
3org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,
4org.springframework.boot.context.ContextIdApplicationContextInitializer,
5org.springframework.boot.context.config.DelegatingApplicationContextInitializer,
6org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,
7org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
spring-boot-autoconfigure項目下
1# Initializers
2org.springframework.context.ApplicationContextInitializer=
3org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,
4org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

image.png
具體的加載方法為 `getSpringFacotiesInstance()`方法,我們進入查看
先進入
SpringFactoriesLoader.loadFactoryNames(type, classLoader)中具體查看加載文件的過程.

image.png
然後我們來看下 loadSpringFactories方法

image.png
通過Debug的方式查看會更清楚哦

image.png
通過 loadSpringFactories 方法我們看到把 spring.factories文件中的所有信息都加載到了內存中了,但是我們現在只需要加載
ApplicationContextInitializer類型的數據。這時我們再通過 getOrDefault()方法來查看。

image.png
進入方法中查看

image.png
然後會根據反射獲取對應的實例對象。

image.png

image.png
好了到這其實我們就清楚了
getSpringFactoriesInstances方法的作用就是幫我們獲取定義在 META-INF/spring.factories文件中的可以為
ApplicationContextInitializer 的值。並通過反射的方式獲取實例對象。然後把實例的對象信息存儲在了SpringApplication的 initializers屬性中。

image.png
3.setListeners
清楚了 setInitializers()方法的作用後,再看 setListeners()方法就非常簡單了,都是調用了
getSpringFactoriesInstances方法,只是傳入的類型不同。也就是要獲取的 META-INF/spring.factories文件中定義的不同信息罷了。

image.png
即加載定義在 META-INF/spring.factories文件中聲明的所有的監聽器,並將獲取後的監聽器存儲在了 SpringApplication的 listeners屬性中。

image.png
默認加載的監聽器為:

image.png
4.mainApplicationClass
最後我們來看下
duduceMainApplicaitonClass()方法是如何反推導出main方法所在的Class對象的。通過源碼我們可以看到是通過 StackTrace來實現的。
1StackTrace:
2我們在學習函數調用時,都知道每個函數都擁有自己的棧空間。
3一個函數被調用時,就創建一個新的棧空間。那麼通過函數的嵌套調用最後就形成了一個函數調用堆棧
StackTrace其實就是記錄了程序方法執行的鏈路。通過Debug方式可以更直觀的來呈現。

image.png
那麼相關的調用鏈路我們都可以獲取到,剩下的就只需要獲取每鏈路判斷執行的方法名稱是否是 main就可以了。

image.png
好了到此相關的4個核心步驟就給大家分析完了,希望對大家能有所幫助哦!
原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/223447.html
微信掃一掃
支付寶掃一掃