Spring Boot超詳細(xì)分析啟動(dòng)流程
一、Spring Boot 工程結(jié)構(gòu)
下載Spring Boot工程源碼, 下載地址
模塊代碼結(jié)構(gòu):

比較重要的是Spring-boot、Spring-boot-autoconfigure以及Spring-boot-starters等組件。
工程模塊介紹
spring-boot
spring boot框架核心插件,對(duì)其他模塊提供主要支持。內(nèi)部包含所熟知的SpringApplication類, 提供應(yīng)用程序入口, 內(nèi)嵌支持Tomcat、Jetty和Undertow等容器。
spring-boot-actuator
主要用于管理和監(jiān)控應(yīng)用, 支持http、jmx、ssh、telnet等連接管理方式。
包含審計(jì)、健康狀態(tài)、數(shù)據(jù)采集等功能。
spring-boot-actuator-autoconfigure
spring-boot-actuator的擴(kuò)展、為其提供自動(dòng)化配置功能。
spring-boot-autoconfigure
實(shí)現(xiàn)spring-boot工程的自動(dòng)化配置, 我們常用的@EnableAutoConfiguration標(biāo)注,就是通過(guò)此工程實(shí)現(xiàn),
觸發(fā)Spring上下文的自動(dòng)裝配。 設(shè)計(jì)目的是減少開(kāi)發(fā)者的對(duì)bean及應(yīng)用組件的管理配置,專注自己的實(shí)現(xiàn)。
spring-boot-cli
提供Spring項(xiàng)目相關(guān)的命令行功能,
安裝CLI相關(guān)工具。即可通過(guò)spring run hello.groovy
直接運(yùn)行Groovy腳本, 不過(guò)過(guò)多的繁瑣配置, 開(kāi)發(fā)人員只需關(guān)注業(yè)務(wù)邏輯。
spring-boot-dependencies
Spring Boot項(xiàng)目的maven依賴管理工程, 定義管理各組件的版本號(hào),
內(nèi)部沒(méi)有具體代碼實(shí)現(xiàn)。
spring-boot-devtools
Spring Boot的開(kāi)發(fā)工具,比如經(jīng)常調(diào)試程序,使用該插件可以支持熱部署,
不需反復(fù)重啟, 提高開(kāi)發(fā)效率。
spring-boot-docs
Spring Boot的文檔配置工程,設(shè)置文檔格式、樣式、布局等。
spring-boot-parent
Spring Boot的父級(jí)工程, 沒(méi)有代碼實(shí)現(xiàn), 主要通過(guò)dependencyManagement管理各子項(xiàng)目的maven組件依賴。
spring-boot-properties-migrator
Spring Boot的配置屬性監(jiān)察功能, 也就是通過(guò)監(jiān)聽(tīng)器觀察指定的屬性KEY, 發(fā)生變化時(shí),符合指定的匹配規(guī)則,將會(huì)觸發(fā)監(jiān)聽(tīng)事件, 執(zhí)行日志或發(fā)送報(bào)告等。
spring-boot-starters
它是一個(gè)管理工程, 里面包含各種應(yīng)用組件,例如我們常用的spring-boot-starter-web組件, 提供對(duì)web服務(wù)支持;spring-boot-starter-data-jdbc組件, 提供的jdbc數(shù)據(jù)源的封裝使用。里面每個(gè)組件只存在一個(gè)pom文件, 引入第三方依賴, 這樣能簡(jiǎn)化配置, 靈活管理, 保障服務(wù)工程的兼容性。
spring-boot-test
里面包含各種模塊及標(biāo)注, 幫助我們方便測(cè)試spring boot 應(yīng)用程序。
spring-boot-test-autoconfigure
服務(wù)于spring-boot-test工程,提供自動(dòng)化配置,便于集成使用。
spring-boot-tools
spring boot工程的管理工具, 比如ant和maven構(gòu)建、文檔配置工具等。
二、Spring Boot 啟動(dòng)流程
Spring Boot 的整體啟動(dòng)流程:

三、Spring Boot 啟動(dòng)流程源碼剖析
1、創(chuàng)建一個(gè)Spring Boot 工程
創(chuàng)建spring-boot-startup工程作為源碼研究。

POM依賴:
<!-- Spring Boot Web 依賴組件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
2、SpringBootApplication啟動(dòng)入口
com.mirson.spring.boot.research.startup.ResearchApplication, 啟動(dòng)類代碼:
@SpringBootApplication
@ComponentScan(basePackages = {"com.mirson"})
public class ResearchApplication {
public static void main(String[] args) {
SpringApplication.run(ResearchApplication.class, args);
}
}核心@SpringBootApplication注解
@Target(ElementType.TYPE) // 注解的適用范圍,其中TYPE用于描述類、接口(包括包注解類型)或enum聲明
@Retention(RetentionPolicy.RUNTIME) // 注解的生命周期,保留到class文件中(三個(gè)生命周期)
@Documented // 表明這個(gè)注解應(yīng)該被javadoc記錄
@Inherited // 子類可以繼承該注解
@SpringBootConfiguration // 繼承了Configuration,表示當(dāng)前是注解類
@EnableAutoConfiguration // 開(kāi)啟springboot的注解功能,springboot的四大神器之一,其借助@import的幫助
@ComponentScan(excludeFilters = { // 掃描路徑設(shè)置,excludeFilters為排除的過(guò)濾器,啟動(dòng)時(shí)不加載
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}打開(kāi)其內(nèi)部, 實(shí)質(zhì)上就是@Configuration注解, 它是負(fù)責(zé)處理JavaConfig配置, 類似之前Spring在XML中定義的各種配置, 通過(guò)@Configuration注解, 會(huì)自動(dòng)加載各種基于JavaConfig實(shí)現(xiàn)的配置。比如@Bean定義, @Autowire自動(dòng)裝配等。
EnableAutoConfiguration
Spring Boot 內(nèi)部封裝了很多組件, 比如異步任務(wù), 緩存, 數(shù)據(jù)源等, EnableAutoConfiguration相當(dāng)于是個(gè)總開(kāi)關(guān), 負(fù)責(zé)管理所有組件的自動(dòng)化配置,它會(huì)去掃描當(dāng)前路徑下所有JavaConfig配置,并且通過(guò)AutoConfigurationImportSelector, 加載默認(rèn)的自動(dòng)化配置組件。
ComponentScan
ComponentScan功能其實(shí)是自動(dòng)掃描并加載符合條件的組件, 比如@Service、@Repository、@Component等, 把它們加載到Spring Ioc容器中。Spring Boot 項(xiàng)目默認(rèn)會(huì)掃描加載啟動(dòng)類所在路徑下的所有JavaConfig配置, 通過(guò)ComponetScan指定package, 可以自定義路徑掃描。
3、Spring Boot 初始化分析
從SpringApplication的run方法進(jìn)入:

創(chuàng)建SpringApplication對(duì)象, 查看構(gòu)造方法:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 設(shè)置資源加載器
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 設(shè)置主資源加載器, 優(yōu)先級(jí)更高
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 應(yīng)用類型推斷處理, 標(biāo)識(shí)應(yīng)用服務(wù)是REACTIVE模式或SERVLET類型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 設(shè)置初始化加載器,默認(rèn)有6個(gè), 作用是讀取spring.factories配置, 每個(gè)初始加載器實(shí)現(xiàn)ApplicationContextInitializer接口
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 設(shè)置監(jiān)聽(tīng)器, 默認(rèn)有10個(gè), 包含配置文件, 日志, Classpath等監(jiān)聽(tīng)器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 設(shè)置啟動(dòng)類信息
this.mainApplicationClass = deduceMainApplicationClass();
}
Initializers初始化加載器

listeners監(jiān)聽(tīng)器

4、Spring Boot 啟動(dòng)深入分析
查看SpringApplication的run方法:
public ConfigurableApplicationContext run(String... args) {
// ① 創(chuàng)建計(jì)時(shí)器
StopWatch stopWatch = new StopWatch();
// 計(jì)時(shí)器開(kāi)始統(tǒng)計(jì)
stopWatch.start();
// ② 定義配置型上下文, 除了具備ApplicationContex, 還擁有生命周期和流屬性
ConfigurableApplicationContext context = null;
// ③ 定義異常記錄報(bào)告
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// ④ 識(shí)別是否Headless模式, 該模式下,系統(tǒng)沒(méi)有顯示設(shè)備、鍵盤或鼠標(biāo)
configureHeadlessProperty();
// ⑤ 獲取所有Spring Boot 內(nèi)置監(jiān)聽(tīng)器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 啟動(dòng)所有監(jiān)聽(tīng)器
listeners.starting();
try {
// ⑥ 設(shè)置服務(wù)啟動(dòng)時(shí)接收的參數(shù)
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 設(shè)置環(huán)境變量信息, 為監(jiān)聽(tīng)器和ConfigurationPropertySources初始環(huán)境變量
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// ⑦ 忽略環(huán)境變量中配置的BEAN信息
configureIgnoreBeanInfo(environment);
// ⑧ 控制臺(tái)打印Banner條, 可以自定義圖片背景等
Banner printedBanner = printBanner(environment);
// ⑨ 創(chuàng)建上下文, 根據(jù)不同模式進(jìn)行創(chuàng)建(SERVLET、REACTIVE)
context = createApplicationContext();
// ⑩ 獲取Spring Factory加載時(shí)的異常報(bào)告
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// ? Spring上下文加載預(yù)處理
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// ? Spring 上下文加載處理
refreshContext(context);
// ? Spring 上下文加載完畢的后置處理, 內(nèi)部暫為空實(shí)現(xiàn)
afterRefresh(context, applicationArguments);
// 停止時(shí)間計(jì)時(shí)器
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// ? 啟動(dòng)監(jiān)聽(tīng)器
listeners.started(context);
// ? Spring Boot 容器啟動(dòng)完畢后, 調(diào)用ApplicationRunner的run方法, 處理自定義邏輯
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// 執(zhí)行監(jiān)聽(tīng)器
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}① 創(chuàng)建StopWatch,Spring 提供的計(jì)時(shí)器,統(tǒng)計(jì)Spring Boot應(yīng)用啟動(dòng)時(shí)長(zhǎng)信息。
② 定義Spring上下文, 并下面第九處執(zhí)行賦值。
③ 定義異常報(bào)告集合, 收集Spring Factory的初始化異常信息。
④ 配置headless信息, 該模式下,系統(tǒng)沒(méi)有顯示設(shè)備、鍵盤或鼠標(biāo)。
⑤ 獲取啟動(dòng)監(jiān)聽(tīng)器,為一個(gè)集合,可以包含多個(gè)監(jiān)聽(tīng), 實(shí)現(xiàn)SpringApplicationRunListener接口,
該接口定義了容器啟動(dòng)的完整生命周期, 如啟動(dòng)、運(yùn)行、環(huán)境準(zhǔn)備、上下文準(zhǔn)備、加載等。EventPublishingRunListener就是一個(gè)具體實(shí)現(xiàn),
將SpringApplicationRunListener接口監(jiān)聽(tīng)到的事件, 轉(zhuǎn)化為SpringApplicationEvent事件, 注冊(cè)并廣播到所有監(jiān)聽(tīng)器下面。
⑥ 服務(wù)配置處理,創(chuàng)建applicationArguments, 為應(yīng)用的命令行參數(shù), 啟動(dòng)程序時(shí)可以指定, 比如—debug等; prepareEnvrionment根據(jù)參數(shù)加載屬性配置, 包含自定義的屬性配置,像我們常見(jiàn)的application.yml都會(huì)加載進(jìn)去, 默認(rèn)會(huì)加載7個(gè)類型的配置到envrionment中。

⑦ configureIgnoreBeanInfo作用是配置是否忽略beaninfo處理, 默認(rèn)是為true,會(huì)自動(dòng)忽略不檢索加載工程中的beaninfo類信息。
⑧ printBanner打印Spring Boot啟動(dòng)的Banner條, 通過(guò)bannerMode屬性控制是否打印,內(nèi)部有g(shù)etImageBanner和getTextBanner實(shí)現(xiàn), 可以支持圖像與文本。
⑨ 容器創(chuàng)建, 通過(guò)createApplicationContext方法創(chuàng)建Spring容器, 會(huì)根據(jù)應(yīng)用類型, 加載不同上下文處理類。Web Servlet類型會(huì)加載AnnotationConfigServletWebServerApplicationContext; Reactive類型會(huì) 加載AnnotationConfigReactiveWebServerApplicationContext。都是繼承ServletWebServerApplicationContext類, 實(shí)現(xiàn)ConfigurableWebApplicationContext接口。 在不指 定應(yīng)用類型的i情況下,默認(rèn)通過(guò)AnnotationConfigApplicationContext類處理上下文,該類是繼承GenericApplicationContext,實(shí)現(xiàn)ConfigurableApplicationContext接口。
⑩ 初始化exceptionReports集合, 通過(guò)SpringFactory加載,
內(nèi)置有19個(gè)異常分析器, 常見(jiàn)的BeanDefinition定義錯(cuò)誤、NoUnique唯一性約束、PortInUse端口占用等異常都是通過(guò)這些分析器處理拋出。

?上下文加載預(yù)處理,prepareContext方法內(nèi)部實(shí)現(xiàn):

設(shè)置context的environment環(huán)境配置屬性,通過(guò)applyInitializer初始化ApplicationContextInitializer。通知監(jiān)聽(tīng)器告訴上下文預(yù)處理工作完成。 接下來(lái)創(chuàng)建所熟知的beanFactory容器管理工廠, 加載BeanDefinition, 最后通知監(jiān)聽(tīng)器,加載完成。
? 刷新啟動(dòng)Spring容器, 調(diào)用refreshConext方法。深入內(nèi)部,可以看到核心處理流程:
@Override
public void refresh() throws BeansException, IllegalStateException {
// 設(shè)置同步鎖, 防止啟動(dòng)關(guān)閉造成的資源爭(zhēng)搶
synchronized (this.startupShutdownMonitor) {
// Spring 上下文預(yù)加載處理
prepareRefresh();
// 刷新并返回bean工廠信息
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 預(yù)加載bean工廠信息, 如設(shè)定classloader加載器, 接口依賴等。
prepareBeanFactory(beanFactory);
try {
// bean工廠處理, 設(shè)置上下文的BeanPostProcessor和ignoreDependencyInterface等信息
postProcessBeanFactory(beanFactory);
// 調(diào)用所有在BeanFactory中已注冊(cè)的beanFactoryPostProcessor。beanFactoryPostProcessor 是BeanFactory的后置處理器, 對(duì)BeanDefinition對(duì)象修改,如我們?cè)赬ML或標(biāo)注配置的bean定義信息
invokeBeanFactoryPostProcessors(beanFactory);
// 通過(guò)委派機(jī)制注冊(cè)Bean創(chuàng)建時(shí)的Bean Processors對(duì)象 。BeanPostProcessor是Bean后置處理器, 負(fù)責(zé)對(duì)Bean對(duì)象修改, 比如實(shí)現(xiàn)InitializingBean接口的Bean, 通過(guò)afterPropertiesSet方法設(shè)置修改
registerBeanPostProcessors(beanFactory);
// 初始化message source , 比如我們常見(jiàn)的國(guó)際化信息處理。
initMessageSource();
// 初始化事件監(jiān)聽(tīng)器并進(jìn)行廣播??梢詫pplicationListener監(jiān)聽(tīng)的事件發(fā)送至相應(yīng)監(jiān)聽(tīng)器做處理。
initApplicationEventMulticaster();
// 實(shí)現(xiàn)其他的指定Bean或容器處理, 比如GenericApplicationContext, 可以實(shí)現(xiàn)一些自定義UI或Theme。
onRefresh();
// 注冊(cè)所有實(shí)現(xiàn)ApplicationListener接口的監(jiān)聽(tīng)器, 廣播處理相應(yīng)事件。
registerListeners();
// 創(chuàng)建所有非懶加載方式定義單例類。
finishBeanFactoryInitialization(beanFactory);
// 完成容器的加載處理, 善后工作, 清除資源緩存, 發(fā)布完成事件等。
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 銷毀bean信息
destroyBeans();
// 復(fù)位 'active'激活標(biāo)志.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// 清除基本緩存信息, 比如方法屬性聲明緩存、標(biāo)注緩存等。
resetCommonCaches();
}
}
}? 為Spring 容器初始化的后置處理方法,預(yù)置功能,內(nèi)部為空實(shí)現(xiàn)。
? Spring容器啟動(dòng)完成后, 通知SpringApplicationListener監(jiān)聽(tīng)數(shù)組,
觸發(fā)容器加載完成started事件, 執(zhí)行監(jiān)聽(tīng)邏輯。
? 再調(diào)用運(yùn)行器, 檢查ApplicationContext中有無(wú)定義,調(diào)用ApplicationRunner、CommandLineRunner接口的run方法。
最后, 調(diào)用所有定義的SpringApplicationListener監(jiān)聽(tīng)器,觸發(fā)容器正常運(yùn)行Running事件, 執(zhí)行監(jiān)聽(tīng)邏輯。
四、總結(jié)
Spring Boot 能夠極為簡(jiǎn)化的開(kāi)發(fā)與配置, 從啟動(dòng)流程的研究分析, Spring Boot 做了大量的封裝與自動(dòng)化處理, 通過(guò)掃描Spring Factory 能夠加載各種自動(dòng)化組件, 同時(shí)內(nèi)置了監(jiān)聽(tīng)器與各種事件, 以及ApplicationRunner啟動(dòng)器, 具有較強(qiáng)的靈活性與擴(kuò)展性, Spring Boot 內(nèi)部封裝簡(jiǎn)潔, 邏輯清晰,沒(méi)有過(guò)多的冗余代碼, 能夠起到很好的借鑒學(xué)習(xí)作用。
到此這篇關(guān)于Spring Boot超詳細(xì)分析啟動(dòng)流程的文章就介紹到這了,更多相關(guān)Spring Boot啟動(dòng)流程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot執(zhí)行有返回值的異步任務(wù)問(wèn)題
這篇文章主要介紹了SpringBoot執(zhí)行有返回值的異步任務(wù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
SpringCloud2020 bootstrap 配置文件失效的解決方法
SpringBoot將Spring fox更換為Springdoc的方法詳解
Java驗(yàn)證日期時(shí)間字符串是否合法的三種方式
Netty源碼分析NioEventLoop處理IO事件相關(guān)邏輯
使用springboot開(kāi)發(fā)的第一個(gè)web入門程序的實(shí)現(xiàn)
mybatis-plus3.4.0邏輯刪除報(bào)錯(cuò)的解決

