SpringBoot啟動(dòng)過(guò)程與自動(dòng)配置過(guò)程解讀
一、Spring Boot 啟動(dòng)流程
Spring Boot 應(yīng)用的入口是 main 方法中的 SpringApplication.run(),但這行代碼背后藏著一整套“初始化-配置-啟動(dòng)”的流水線。
我們可以將其分為構(gòu)造階段和運(yùn)行階段兩大步,每一步都有明確的職責(zé)邊界。
1.1 構(gòu)造階段:初始化核心組件
SpringApplication 實(shí)例的創(chuàng)建是啟動(dòng)的第一步,這個(gè)階段的核心是“搭骨架”——確定應(yīng)用類(lèi)型、加載初始化器和監(jiān)聽(tīng)器,為后續(xù)運(yùn)行鋪路。
- 驗(yàn)證主類(lèi)合法性:通過(guò)
Assert.notNull確保傳入的主配置類(lèi)(標(biāo)注@SpringBootApplication的類(lèi))非空,這是整個(gè)應(yīng)用的“根”。 - 推斷應(yīng)用類(lèi)型:通過(guò)
WebApplicationType.deduceFromClasspath()檢查類(lèi)路徑中是否存在DispatcherServlet(MVC)或DispatcherHandler(WebFlux),自動(dòng)判斷是SERVLET、REACTIVE還是非 Web 應(yīng)用(NONE)。 - 加載初始化器與監(jiān)聽(tīng)器:從
META-INF/spring.factories中讀取ApplicationContextInitializer和ApplicationListener的實(shí)現(xiàn)類(lèi)。這些組件是 Spring Boot 的“擴(kuò)展點(diǎn)”,例如初始化器可用于預(yù)先配置上下文,監(jiān)聽(tīng)器可監(jiān)聽(tīng)啟動(dòng)各階段事件。 - 定位主程序類(lèi):通過(guò)棧追蹤找到調(diào)用
main方法的類(lèi),作為應(yīng)用的入口標(biāo)記。
關(guān)鍵源碼片段:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.setInitializers(getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
1.2 運(yùn)行階段:從環(huán)境準(zhǔn)備到容器啟動(dòng)
SpringApplication.run() 方法是真正的“執(zhí)行引擎”,可細(xì)分為 10 個(gè)關(guān)鍵步驟,環(huán)環(huán)相扣:
- 啟動(dòng)監(jiān)控與監(jiān)聽(tīng)器:通過(guò)
StopWatch記錄啟動(dòng)時(shí)間,同時(shí)觸發(fā)SpringApplicationRunListeners.starting(),通知所有監(jiān)聽(tīng)器“應(yīng)用開(kāi)始啟動(dòng)”。 - 環(huán)境配置:創(chuàng)建
ConfigurableEnvironment(根據(jù)應(yīng)用類(lèi)型選擇StandardServletEnvironment或StandardReactiveWebEnvironment),并加載系統(tǒng)變量、配置文件(如application.yml)等。完成后調(diào)用environmentPrepared事件,允許監(jiān)聽(tīng)器對(duì)環(huán)境進(jìn)行二次調(diào)整。 - 打印 Banner:默認(rèn)打印 Spring Boot 版本信息,可通過(guò)
resources/banner.txt自定義(支持 ASCII 藝術(shù)或變量替換)。 - 創(chuàng)建應(yīng)用上下文:根據(jù)應(yīng)用類(lèi)型生成對(duì)應(yīng)的 IOC 容器,例如 Servlet 應(yīng)用使用
AnnotationConfigServletWebServerApplicationContext。 - 上下文準(zhǔn)備:將環(huán)境、命令行參數(shù)等注入上下文,執(zhí)行初始化器的
initialize方法,并觸發(fā)contextPrepared事件。 - 上下文刷新:調(diào)用
refreshContext,觸發(fā) Spring 容器的核心流程(Bean 定義加載、實(shí)例化、依賴(lài)注入等),同時(shí)啟動(dòng)嵌入式服務(wù)器(如 Tomcat)。 - 收尾工作:執(zhí)行
afterRefresh(預(yù)留擴(kuò)展點(diǎn)),停止計(jì)時(shí)器,并觸發(fā)started事件。 - 回調(diào)自定義邏輯:從容器中獲取
ApplicationRunner和CommandLineRunner的實(shí)現(xiàn)類(lèi),按順序執(zhí)行其run方法(適合啟動(dòng)后初始化數(shù)據(jù)等操作)。 - 啟動(dòng)完成:觸發(fā)
running事件,應(yīng)用正式就緒。 - 異常處理:若啟動(dòng)失敗,調(diào)用
failed事件,打印異常并關(guān)閉上下文。
實(shí)戰(zhàn)技巧:若需在啟動(dòng)后執(zhí)行初始化邏輯(如加載緩存),優(yōu)先使用 ApplicationRunner(支持 ApplicationArguments 解析)而非 CommandLineRunner(直接接收字符串?dāng)?shù)組)。
二、自動(dòng)配置:Spring Boot 的“智能配置”核心
Spring Boot 最令人稱(chēng)道的“自動(dòng)配置”,本質(zhì)是“基于條件的約定配置”。
它通過(guò)一套規(guī)則判斷環(huán)境中存在的依賴(lài),自動(dòng)裝配對(duì)應(yīng)的組件,避免手動(dòng)編寫(xiě) XML 或 @Bean 配置。
2.1 自動(dòng)配置的底層原理
自動(dòng)配置的觸發(fā)點(diǎn)是 @EnableAutoConfiguration 注解(被 @SpringBootApplication 間接包含),其核心邏輯在 AutoConfigurationImportSelector 中:
- 加載候選配置類(lèi):通過(guò)
SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, classLoader),掃描所有 Jar 包的META-INF/spring.factories,獲取EnableAutoConfiguration對(duì)應(yīng)的配置類(lèi)列表(如HttpEncodingAutoConfiguration、DataSourceAutoConfiguration等)。 - 過(guò)濾與去重:移除重復(fù)或被排除的配置類(lèi)(通過(guò)
@SpringBootApplication(exclude = ...)指定),再通過(guò)條件注解(如@ConditionalOnClass)判斷配置類(lèi)是否生效。 - 注入容器:將生效的配置類(lèi)加載到容器中,其內(nèi)部的
@Bean方法會(huì)自動(dòng)生成組件。
關(guān)鍵源碼片段:
// AutoConfigurationImportSelector 中加載候選配置類(lèi)的邏輯
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories");
return configurations;
}
2.2 條件注解:自動(dòng)配置的“開(kāi)關(guān)”
自動(dòng)配置的“智能”依賴(lài)于 @Conditional 系列注解,它們決定了一個(gè)配置類(lèi)或 @Bean 方法是否生效。
常用條件注解如下:
| 注解 | 作用 | 示例場(chǎng)景 |
|---|---|---|
| @ConditionalOnClass | 類(lèi)路徑存在指定類(lèi)時(shí)生效 | 檢測(cè)到 DataSource.class 時(shí)配置數(shù)據(jù)源 |
| @ConditionalOnMissingBean | 容器中不存在指定 Bean 時(shí)生效 | 若用戶未定義 RestTemplate,則自動(dòng)配置默認(rèn)實(shí)例 |
| @ConditionalOnProperty | 配置項(xiàng)滿足條件時(shí)生效 | 通過(guò) spring.datasource.enabled=true 控制數(shù)據(jù)源是否啟用 |
| @ConditionalOnWebApplication | 僅 Web 應(yīng)用生效 | 配置 DispatcherServlet |
實(shí)戰(zhàn)案例:
HttpEncodingAutoConfiguration(HTTP 編碼自動(dòng)配置)的條件判斷:
@Configuration
@EnableConfigurationProperties(HttpProperties.class) // 綁定配置文件屬性
@ConditionalOnWebApplication(type = Type.SERVLET) // 僅 Servlet 應(yīng)用生效
@ConditionalOnClass(CharacterEncodingFilter.class) // 存在字符編碼過(guò)濾器類(lèi)時(shí)生效
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
@Bean
@ConditionalOnMissingBean // 若用戶未定義,則自動(dòng)配置
public CharacterEncodingFilter characterEncodingFilter() {
// 從 HttpProperties 中讀取配置(如 spring.http.encoding.charset=utf-8)
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(properties.getCharset().name());
return filter;
}
}
技巧:
若需禁用某個(gè)自動(dòng)配置(如默認(rèn)的數(shù)據(jù)源配置),可通過(guò)
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
或配置
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
2.3 自定義自動(dòng)配置:擴(kuò)展 Spring Boot
若需為自己的組件實(shí)現(xiàn)自動(dòng)配置(如公司內(nèi)部 SDK),需遵循以下步驟:
編寫(xiě)配置類(lèi):使用 @Configuration 和條件注解定義組件裝配邏輯,通過(guò) @EnableConfigurationProperties 綁定配置屬性。
注冊(cè)配置類(lèi):在 Jar 包的 META-INF/spring.factories 中添加:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.sdk.MyComponentAutoConfiguration
添加配置元數(shù)據(jù):在 META-INF/spring-configuration-metadata.json 中定義配置項(xiàng)的描述(IDE 會(huì)自動(dòng)提示),例如:
{
"properties": [
{
"name": "example.sdk.enabled",
"type": "java.lang.Boolean",
"defaultValue": true,
"description": "是否啟用 SDK 組件"
}
]
}
驗(yàn)證方法:?jiǎn)?dòng)應(yīng)用時(shí)添加 --debug 參數(shù),控制臺(tái)會(huì)打印自動(dòng)配置報(bào)告,顯示哪些配置類(lèi)生效(Positive matches)或不生效(Negative matches)。
三、總結(jié):Spring Boot 便捷性的本質(zhì)
Spring Boot 的啟動(dòng)流程和自動(dòng)配置,本質(zhì)是“約定優(yōu)于配置”的極致體現(xiàn):
- 啟動(dòng)流程通過(guò)標(biāo)準(zhǔn)化的步驟,將 Spring 容器的初始化與嵌入式服務(wù)器的啟動(dòng)無(wú)縫銜接,減少手動(dòng)干預(yù)。
- 自動(dòng)配置基于條件注解和
spring.factories機(jī)制,實(shí)現(xiàn)了“依賴(lài)驅(qū)動(dòng)配置”,讓開(kāi)發(fā)者專(zhuān)注于業(yè)務(wù)邏輯而非框架整合。
理解這些原理后,不僅能更高效地排查問(wèn)題(如啟動(dòng)慢、自動(dòng)配置失效),還能靈活擴(kuò)展 Spring Boot(如自定義 starters 或啟動(dòng)流程)。
記?。篠pring Boot 不是“魔法”,而是對(duì) Spring 框架的“工程化封裝”——看透其源碼,就能真正掌控它。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java反射 PropertyDescriptor類(lèi)案例詳解
這篇文章主要介紹了Java反射 PropertyDescriptor類(lèi)案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
詳解在SpringBoot應(yīng)用中獲取應(yīng)用上下文方法
本篇文章主要介紹了詳解在SpringBoot應(yīng)用中獲取應(yīng)用上下文方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-04-04
Java數(shù)據(jù)結(jié)構(gòu)二叉樹(shù)難點(diǎn)解析
樹(shù)是一種重要的非線性數(shù)據(jù)結(jié)構(gòu),直觀地看,它是數(shù)據(jù)元素(在樹(shù)中稱(chēng)為結(jié)點(diǎn))按分支關(guān)系組織起來(lái)的結(jié)構(gòu),很象自然界中的樹(shù)那樣。樹(shù)結(jié)構(gòu)在客觀世界中廣泛存在,如人類(lèi)社會(huì)的族譜和各種社會(huì)組織機(jī)構(gòu)都可用樹(shù)形象表示2021-10-10
SpringBoot如何整合Springsecurity實(shí)現(xiàn)數(shù)據(jù)庫(kù)登錄及權(quán)限控制
這篇文章主要給大家介紹了關(guān)于SpringBoot如何整合Springsecurity實(shí)現(xiàn)數(shù)據(jù)庫(kù)登錄及權(quán)限控制的相關(guān)資料,文中通過(guò)圖文以及實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-01-01
IDEA POM文件配置profile實(shí)現(xiàn)不同環(huán)境切換的方法步驟
這篇文章主要介紹了IDEA POM文件配置profile實(shí)現(xiàn)不同環(huán)境切換的方法步驟2024-03-03
java實(shí)現(xiàn)簡(jiǎn)單的搜索引擎
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡(jiǎn)單的搜索引擎的相關(guān)資料,需要的朋友可以參考下2016-02-02
Mybatis-plus如何提前獲取實(shí)體類(lèi)用雪花算法生成的ID
本文主要介紹了Mybatis-plus如何提前獲取實(shí)體類(lèi)用雪花算法生成的ID,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
基于Java實(shí)現(xiàn)PDF文本旋轉(zhuǎn)傾斜
這篇文章主要介紹了基于Java實(shí)現(xiàn)PDF文本旋轉(zhuǎn)傾斜,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05

