Java?SpringBoot核心源碼詳解
SpringBoot源碼主線分析
我們要分析一個(gè)框架的源碼不可能通過(guò)一篇文章就搞定的,本文我們就來(lái)分析下SpringBoot源碼中的主線流程。先掌握SpringBoot項(xiàng)目啟動(dòng)的核心操作,然后我們?cè)偕钊朊恳粋€(gè)具體的實(shí)現(xiàn)細(xì)節(jié),注:本系列源碼都以SpringBoot2.2.5.RELEASE版本來(lái)講解
1.SpringBoot啟動(dòng)的入口
當(dāng)我們啟動(dòng)一個(gè)SpringBoot項(xiàng)目的時(shí)候,入口程序就是main方法,而在main方法中就執(zhí)行了一個(gè)run方法。
@SpringBootApplication
public class StartApp {
public static void main(String[] args) {
SpringApplication.run(StartApp.class);
}
}
2.run方法
然后我們進(jìn)入run()方法中看。代碼比較簡(jiǎn)單
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
// 調(diào)用重載的run方法,將傳遞的Class對(duì)象封裝為了一個(gè)數(shù)組
return run(new Class<?>[] { primarySource }, args);
}
調(diào)用了重載的一個(gè)run()方法,將我們傳遞進(jìn)來(lái)的類(lèi)對(duì)象封裝為了一個(gè)數(shù)組,僅此而已。我們?cè)龠M(jìn)入run()方法。
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
// 創(chuàng)建了一個(gè)SpringApplication對(duì)象,并調(diào)用其run方法
// 1.先看下構(gòu)造方法中的邏輯
// 2.然后再看run方法的邏輯
return new SpringApplication(primarySources).run(args);
}
在該方法中創(chuàng)建了一個(gè)SpringApplication對(duì)象。同時(shí)調(diào)用了SpringApplication對(duì)象的run方法。這里的邏輯有分支,先看下SpringApplication的構(gòu)造方法中的邏輯
3.SpringApplication構(gòu)造器
我們進(jìn)入SpringApplication的構(gòu)造方法,看的核心代碼為
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 傳遞的resourceLoader為null
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 記錄主方法的配置類(lèi)名稱(chēng)
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 記錄當(dāng)前項(xiàng)目的類(lèi)型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 加載配置在spring.factories文件中的ApplicationContextInitializer對(duì)應(yīng)的類(lèi)型并實(shí)例化
// 并將加載的數(shù)據(jù)存儲(chǔ)在了 initializers 成員變量中。
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 初始化監(jiān)聽(tīng)器 并將加載的監(jiān)聽(tīng)器實(shí)例對(duì)象存儲(chǔ)在了listeners成員變量中
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 反推main方法所在的Class對(duì)象 并記錄在了mainApplicationClass對(duì)象中
this.mainApplicationClass = deduceMainApplicationClass();
}
在本方法中完成了幾個(gè)核心操作
1.推斷當(dāng)前項(xiàng)目的類(lèi)型
2.加載配置在spring.factories文件中的ApplicationContextInitializer中的類(lèi)型并實(shí)例化后存儲(chǔ)在了initializers中。
3.和2的步驟差不多,完成監(jiān)聽(tīng)器的初始化操作,并將實(shí)例化的監(jiān)聽(tīng)器對(duì)象存儲(chǔ)在了listeners成員變量中
4.通過(guò)StackTrace反推main方法所在的Class對(duì)象
上面的核心操作具體的實(shí)現(xiàn)細(xì)節(jié)我們?cè)诤竺娴脑敿?xì)文章會(huì)給大家剖析
4.run方法
接下來(lái)我們?cè)诨氐絊pringApplication.run()方法中。
public ConfigurableApplicationContext run(String... args) {
// 創(chuàng)建一個(gè)任務(wù)執(zhí)行觀察器
StopWatch stopWatch = new StopWatch();
// 開(kāi)始執(zhí)行記錄執(zhí)行時(shí)間
stopWatch.start();
// 聲明 ConfigurableApplicationContext 對(duì)象
ConfigurableApplicationContext context = null;
// 聲明集合容器用來(lái)存儲(chǔ) SpringBootExceptionReporter 啟動(dòng)錯(cuò)誤的回調(diào)接口
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 設(shè)置了一個(gè)名為java.awt.headless的系統(tǒng)屬性
// 其實(shí)是想設(shè)置該應(yīng)用程序,即使沒(méi)有檢測(cè)到顯示器,也允許其啟動(dòng).
//對(duì)于服務(wù)器來(lái)說(shuō),是不需要顯示器的,所以要這樣設(shè)置.
configureHeadlessProperty();
// 獲取 SpringApplicationRunListener 加載的是 EventPublishingRunListener
// 獲取啟動(dòng)時(shí)到監(jiān)聽(tīng)器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 觸發(fā)啟動(dòng)事件
listeners.starting();
try {
// 構(gòu)造一個(gè)應(yīng)用程序的參數(shù)持有類(lèi)
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 創(chuàng)建并配置環(huán)境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 配置需要忽略的BeanInfo信息
configureIgnoreBeanInfo(environment);
// 輸出的Banner信息
Banner printedBanner = printBanner(environment);
// 創(chuàng)建應(yīng)用上下文對(duì)象
context = createApplicationContext();
// 加載配置的啟動(dòng)異常處理器
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 刷新前操作
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新應(yīng)用上下文 完成Spring容器的初始化
refreshContext(context);
// 刷新后操作
afterRefresh(context, applicationArguments);
// 結(jié)束記錄啟動(dòng)時(shí)間
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 事件廣播 啟動(dòng)完成了
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// 事件廣播啟動(dòng)出錯(cuò)了
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// 監(jiān)聽(tīng)器運(yùn)行中
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
// 返回上下文對(duì)象--> Spring容器對(duì)象
return context;
}
在這個(gè)方法中完成了SpringBoot項(xiàng)目啟動(dòng)的很多核心的操作,我們來(lái)總結(jié)下上面的步驟
- 創(chuàng)建了一個(gè)任務(wù)執(zhí)行的觀察器,統(tǒng)計(jì)啟動(dòng)的時(shí)間
- 聲明
ConfigurableApplicationContext對(duì)象 - 聲明集合容器來(lái)存儲(chǔ)
SpringBootExceptionReporter即啟動(dòng)錯(cuò)誤的回調(diào)接口 - 設(shè)置
java.awt.headless的系統(tǒng)屬性 - 獲取我們之間初始化的監(jiān)聽(tīng)器(
EventPublishingRunListener),并觸發(fā)starting事件 - 創(chuàng)建
ApplicationArguments這是一個(gè)應(yīng)用程序的參數(shù)持有類(lèi) - 創(chuàng)建
ConfigurableEnvironment這時(shí)一個(gè)配置環(huán)境的對(duì)象 - 配置需要忽略的
BeanInfo信息 - 配置Banner信息對(duì)象
- 創(chuàng)建對(duì)象的上下文對(duì)象
- 加載配置的啟動(dòng)異常的回調(diào)異常處理器
- 刷新應(yīng)用上下文,本質(zhì)就是完成Spring容器的初始化操作
- 啟動(dòng)結(jié)束記錄啟動(dòng)耗時(shí)
- 完成對(duì)應(yīng)的事件廣播
- 返回應(yīng)用上下文對(duì)象。
到此SpringBoot項(xiàng)目的啟動(dòng)初始化的代碼的主要流程就介紹完成了。細(xì)節(jié)部分后面詳細(xì)講解。
總結(jié)
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
SpringCloud Eureka 服務(wù)注冊(cè)實(shí)現(xiàn)過(guò)程
這篇文章主要介紹了SpringCloud Eureka 服務(wù)注冊(cè)實(shí)現(xiàn)過(guò)程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
Spring Cloud Feign接口返回流的實(shí)現(xiàn)
這篇文章主要介紹了Spring Cloud Feign接口返回流的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
在idea環(huán)境下構(gòu)建springCloud項(xiàng)目
本篇文章主要介紹了在idea環(huán)境下構(gòu)建springCloud項(xiàng)目,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11
基于SSM框架+Javamail發(fā)送郵件的代碼實(shí)例
本篇文章主要介紹了基于SSM框架+Javamail發(fā)送郵件的代碼實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2016-12-12
SpringMVC實(shí)現(xiàn)簡(jiǎn)單跳轉(zhuǎn)方法(專(zhuān)題)
這篇文章主要介紹了SpringMVC實(shí)現(xiàn)簡(jiǎn)單跳轉(zhuǎn)方法(專(zhuān)題),詳細(xì)的介紹了SpringMVC跳轉(zhuǎn)的幾種方法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2018-03-03
HttpMessageConverter報(bào)文信息轉(zhuǎn)換器的深入講解
在Spring中內(nèi)置了大量的HttpMessageConverter,通過(guò)請(qǐng)求頭信息中的MIME類(lèi)型,選擇相應(yīng)的HttpMessageConverter,這篇文章主要給大家介紹了關(guān)于HttpMessageConverter報(bào)文信息轉(zhuǎn)換器的相關(guān)資料,需要的朋友可以參考下2022-01-01
Java實(shí)現(xiàn)經(jīng)典角色扮演偵探游戲游戲的示例代碼
這篇文章主要介紹了如何利用Java語(yǔ)言自制一個(gè)偵探文字游戲—《角色扮演偵探》,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編學(xué)習(xí)一下2022-02-02

