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

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

@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 傳遞的resourceLoader為null
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 記錄主方法的配置類名稱
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 記錄當前專案的型別
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 加載配置在spring.factories檔案中的ApplicationContextInitializer對應的型別并實體化
// 并將加載的資料存盤在了 initializers 成員變數中,
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 初始化監聽器 并將加載的監聽器實體物件存盤在了listeners成員變數中
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 反推main方法所在的Class物件 并記錄在了mainApplicationClass物件中
this.mainApplicationClass = deduceMainApplicationClass();
}
1.webApplicationType
??首先來看下webApplicationType是如何來推匯出當前啟動的專案的型別,通過代碼可以看到是通過deduceFromClassPath()方法根據ClassPath來推匯出來的,
this.webApplicationType = WebApplicationType.deduceFromClasspath();
??跟蹤進去看代碼
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
??在看整體的實作邏輯之前,我們先分別看兩個內容,第一就是在上面的代碼中使用到了相關的靜態變數,

??這些靜態變數其實就是一些系結的Java類的全類路徑,第二個就是 ClassUtils.isPresent()方法,該方法的邏輯也非常簡單,就是通過反射的方式獲取對應的型別的Class物件,如果存在回傳true,否則回傳false

??所以到此推導的邏輯就非常清楚了

2.setInitializers
??然后我們再來看下如何實作加載初始化器的,
// 加載配置在spring.factories檔案中的ApplicationContextInitializer對應的型別并實體化
// 并將加載的資料存盤在了 initializers 成員變數中,
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
??首先所有的初始化器都實作了 ApplicationContextInitializer介面,也就是根據這個型別來加載相關的實作類,
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
void initialize(C var1);
}
??然后加載的關鍵方法是 getSpringFactoriesInstances()方法,該方法會加載 spring.factories檔案中的key為 org.springframework.context.ApplicationContextInitializer 的值,
spring-boot專案下
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
spring-boot-autoconfigure專案下
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

??具體的加載方法為 getSpringFacotiesInstance()方法,我們進入查看
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
// 獲取當前背景關系類加載器
ClassLoader classLoader = getClassLoader();
// 獲取到的擴展類名存入set集合中防止重復
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 創建擴展點實體
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
??先進入 SpringFactoriesLoader.loadFactoryNames(type, classLoader)中具體查看加載檔案的程序.

??然后我們來看下 loadSpringFactories方法

??通過Debug的方式查看會更清楚哦

??通過 loadSpringFactories 方法我們看到把 spring.factories檔案中的所有資訊都加載到了記憶體中了,但是我們現在只需要加載 ApplicationContextInitializer型別的資料,這時我們再通過 getOrDefault()方法來查看,

??進入方法中查看

??然后會根據反射獲取對應的實體物件,


??好了到這其實我們就清楚了 getSpringFactoriesInstances方法的作用就是幫我們獲取定義在 META-INF/spring.factories檔案中的可以為 ApplicationContextInitializer 的值,并通過反射的方式獲取實體物件,然后把實體的物件資訊存盤在了SpringApplication的 initializers屬性中,

3.setListeners
??清楚了 setInitializers()方法的作用后,再看 setListeners()方法就非常簡單了,都是呼叫了 getSpringFactoriesInstances方法,只是傳入的型別不同,也就是要獲取的 META-INF/spring.factories檔案中定義的不同資訊罷了,

??即加載定義在 META-INF/spring.factories檔案中宣告的所有的監聽器,并將獲取后的監聽器存盤在了 SpringApplication的 listeners屬性中,

??默認加載的監聽器為:

4.mainApplicationClass
??最后我們來看下 duduceMainApplicaitonClass()方法是如何反推匯出main方法所在的Class物件的,通過原始碼我們可以看到是通過 StackTrace來實作的,
StackTrace: 我們在學習函式呼叫時,都知道每個函式都擁有自己的堆疊空間, 一個函式被呼叫時,就創建一個新的堆疊空間,那么通過函式的嵌套呼叫最后就形成了一個函式呼叫堆疊
??StackTrace其實就是記錄了程式方法執行的鏈路,通過Debug方式可以更直觀的來呈現,

??那么相關的呼叫鏈路我們都可以獲取到,剩下的就只需要獲取每鏈路判斷執行的方法名稱是否是 main就可以了,

??好了到此相關的4個核心步驟就給大家分析完了,希望對大家能有所幫助哦!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/392097.html
標籤:java
