Spring Boot 啟動原理揭秘:從 main 方法到自動裝配
引言
Spring Boot 作為 Java 領域最流行的開發框架之一,憑借其“開箱即用”的特性極大地簡化了 Spring 應用的搭建和部署。然而,盡管開發者在日常工作中頻繁使用 Spring Boot 的啟動類(main
方法)和自動裝配機制,真正理解其底層原理的人卻并不多。本文將深入剖析 Spring Boot 的啟動流程,從 main
方法入手,逐步揭示 Spring Boot 是如何通過自動裝配實現零配置的奇跡的。
我們將按照以下結構展開:
- Spring Boot 簡介與核心優勢
- Spring Boot 應用的基本結構
- Spring Boot 的 main 方法分析
- Spring Boot 啟動流程概述
- SpringApplication 類的作用與初始化過程
- Spring Boot 的上下文環境(ApplicationContext)創建過程
- Spring Boot 的自動裝配機制詳解
- Spring Boot 的條件化裝配(Conditional On)機制
- Spring Boot 啟動過程中的事件監聽機制
- Spring Boot 啟動過程中常見的擴展點與自定義配置
- 總結與最佳實踐
接下來,我們將逐一解析這些內容,幫助你全面掌握 Spring Boot 的啟動原理。
Spring Boot 簡介與核心優勢
什么是 Spring Boot?
Spring Boot 是由 Pivotal 團隊推出的一個開源框架,旨在簡化 Spring 應用的初始搭建和開發。它基于 Spring Framework 構建,提供了一種快速、便捷的方式來構建獨立運行的、生產級的應用程序。Spring Boot 的核心理念是“約定優于配置”,這意味著開發者無需手動編寫大量 XML 或 Java 配置代碼即可快速啟動項目。
Spring Boot 的核心優勢
-
開箱即用(Opinionated Starter Dependencies)
Spring Boot 提供了一系列預配置的依賴項(稱為 “Starter”),例如spring-boot-starter-web
、spring-boot-starter-data-jpa
等。這些依賴項已經集成了常用的庫和默認配置,開發者只需引入對應的 Starter 即可直接使用相關功能,而無需手動配置復雜的依賴關系。 -
內嵌服務器(Embedded Server)
Spring Boot 默認支持內嵌的 Tomcat、Jetty 或 Undertow 服務器,這意味著開發者可以將應用程序打包為一個獨立的 JAR 文件,并直接運行,而無需額外部署到外部應用服務器。這大大簡化了部署流程,提高了開發效率。 -
自動裝配(Auto-Configuration)
Spring Boot 最具代表性的特性之一就是自動裝配。它能夠根據類路徑上的依賴項自動推斷所需的配置,并注冊相應的 Bean。例如,如果項目中包含 H2 數據庫驅動,Spring Boot 會自動配置內存數據庫連接池和相關的 DAO 組件,而無需開發者手動編寫DataSource
配置。 -
零配置(Zero Configuration)
Spring Boot 結合自動裝配和默認約定,使得開發者幾乎不需要編寫任何 XML 或 Java 配置文件即可啟動應用。這種零配置的理念極大降低了學習成本,提高了開發效率。 -
生產就緒(Production Ready)
Spring Boot 提供了許多生產級別的功能,如健康檢查(Health Check)、指標監控(Metrics)、日志管理(Log Management)等。這些功能可以幫助開發者更好地監控和維護應用程序。 -
強大的 CLI 工具(Command Line Interface)
Spring Boot 提供了一個命令行工具(CLI),允許開發者通過簡單的命令快速創建原型應用,甚至可以直接運行 Groovy 腳本而無需編譯。 -
豐富的文檔和社區支持
Spring Boot 擁有龐大的社區和詳盡的官方文檔,開發者可以輕松找到解決方案或最佳實踐。此外,Spring Boot 還與許多其他流行框架(如 Spring Cloud、Spring Security、Spring Data 等)無縫集成,形成了完整的生態系統。 -
靈活的配置方式
Spring Boot 支持多種配置方式,包括application.properties
和application.yml
文件,同時支持外部配置(如環境變量、命令行參數等),便于在不同環境中進行動態調整。
綜上所述,Spring Boot 憑借其簡潔、高效、靈活的特點,成為現代 Java 開發的標準框架之一。接下來,我們將深入了解 Spring Boot 應用的基本結構,以便更清楚地理解它的啟動機制。
Spring Boot 應用的基本結構
一個典型的 Spring Boot 項目通常遵循標準的 Maven 或 Gradle 項目結構,并結合 Spring Boot 的特定約定來組織代碼。為了更好地理解 Spring Boot 的啟動機制,我們需要先了解其基本的項目結構以及各個關鍵組成部分的作用。
1. 項目目錄結構
Spring Boot 項目的標準目錄結構如下所示:
my-springboot-project/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com.example.demo/
│ │ │ ├── DemoApplication.java
│ │ │ ├── controller/
│ │ │ ├── service/
│ │ │ └── repository/
│ │ └── resources/
│ │ ├── application.properties (或 application.yml)
│ │ └── static/
│ │ └── templates/
│ └── test/
│ └── java/
│ └── com.example.demo/
│ └── DemoApplicationTests.java
└── pom.xml (Maven) 或 build.gradle (Gradle)
(1)src/main/java
這是 Java 源代碼存放的位置。通常,主類(包含 main
方法的類)位于包的根目錄下,例如 com.example.demo.DemoApplication
。其余的業務邏輯代碼,如 Controller、Service、Repository 等組件,則分別存放在不同的子包中。
(2)src/main/resources
該目錄存放非 Java 文件資源,主要包括:
application.properties
或application.yml
:Spring Boot 的核心配置文件,用于設置端口號、數據源、日志級別等配置信息。static/
:靜態資源目錄,存放 HTML、CSS、JavaScript、圖片等靜態文件,這些文件會被直接映射到 Web 根路徑下。templates/
:模板引擎資源目錄,適用于 Thymeleaf、Freemarker 等模板引擎,用于渲染動態頁面。
(3)src/test/java
這是單元測試代碼存放的位置。Spring Boot 提供了對測試的良好支持,通常使用 JUnit 編寫測試類,確保代碼質量。
(4)pom.xml
(Maven)或 build.gradle
(Gradle)
這是項目的構建配置文件。Maven 使用 pom.xml
來聲明依賴項和插件,Gradle 則使用 build.gradle
。Spring Boot 項目通常會引入 spring-boot-starter-*
相關的依賴,以啟用對應的功能模塊。
2. 主類(Main Class)
Spring Boot 應用的主類是帶有 @SpringBootApplication
注解的類,并且包含 main
方法。例如:
package com.example.demo;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}
(1)@SpringBootApplication
注解
@SpringBootApplication
是 Spring Boot 中最重要的注解之一,它是一個組合注解,包含了以下三個核心注解:
@SpringBootConfiguration
:表示該類是一個 Spring Boot 的配置類,本質上是@Configuration
的變體。@ComponentScan
:自動掃描并注冊 Bean,通常掃描當前類所在包及其子包下的組件。@EnableAutoConfiguration
:啟用 Spring Boot 的自動裝配機制,這是 Spring Boot 實現零配置的核心功能之一。
(2)main
方法
main
方法是 Java 應用程序的入口點,Spring Boot 通過調用 SpringApplication.run()
方法來啟動應用。該方法會觸發一系列內部機制,包括上下文初始化、自動裝配、Web 服務器啟動等操作。
3. 控制器(Controller)
控制器負責處理 HTTP 請求,通常使用 @RestController
或 @Controller
注解。例如:
@RestController
@RequestMapping("/hello")
public class HelloController {@GetMappingpublic String sayHello() {return "Hello, Spring Boot!";}
}
4. 服務層(Service)
服務層負責處理業務邏輯,通常使用 @Service
注解標注:
@Service
public class HelloService {public String getGreeting() {return "Welcome to Spring Boot!";}
}
5. 數據訪問層(Repository)
數據訪問層負責與數據庫交互,通常使用 @Repository
注解標注:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
6. 啟動流程概覽
當我們執行 DemoApplication.main()
方法時,Spring Boot 會經歷以下幾個主要階段:
- 加載 Spring Boot 的自動裝配機制:根據類路徑上的依賴項,自動配置各類組件。
- 創建 Spring 應用上下文(ApplicationContext):初始化 IoC 容器,注冊 Bean。
- 啟動內嵌 Web 服務器(如 Tomcat):如果是 Web 應用,則啟動 Web 服務器并監聽指定端口。
- 執行 CommandLineRunner 或 ApplicationRunner:如果有自定義的啟動任務,可以在應用啟動完成后執行。
接下來,我們將深入探討 Spring Boot 的 main
方法,分析其背后的執行流程,以及它是如何觸發整個應用的啟動過程的。
Spring Boot 的 main 方法分析
在 Spring Boot 應用程序中,main
方法是整個應用的入口點。雖然它的代碼看起來非常簡單,但背后隱藏著復雜的啟動機制。Spring Boot 通過 SpringApplication.run()
方法實現了高度封裝的啟動流程,使開發者無需關心底層細節即可快速啟動應用。
1. main
方法的典型寫法
一個典型的 Spring Boot 應用程序的 main
方法如下所示:
public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);
}
這段代碼看似簡單,但它實際上觸發了 Spring Boot 應用的完整生命周期。我們可以將其拆分為兩個部分來理解:
SpringApplication
類的構造run()
方法的執行流程
2. SpringApplication
類的構造
SpringApplication
是 Spring Boot 提供的一個核心類,用于引導和配置 Spring 應用上下文。當調用 SpringApplication.run(DemoApplication.class, args)
時,首先會創建一個 SpringApplication
實例。
(1)構造函數源碼分析
public SpringApplication(Class<?> primarySource) {this(null, primarySource);
}public SpringApplication(ResourceLoader resourceLoader, Class<?> primarySource) {this.resourceLoader = resourceLoader;Assert.notNull(primarySource, "Primary source must not be null");this.primarySources = new LinkedHashSet<>();this.primarySources.add(primarySource);this.webApplicationType = WebApplicationType.deduceFromClasspath();setInitializers((List) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((List) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();
}
在這個構造過程中,SpringApplication
會完成以下幾項關鍵操作:
a. 設置主類(Primary Source)
主類(Primary Source)指的是包含 main
方法的類,也就是我們傳入的 DemoApplication.class
。Spring Boot 會利用這個類來確定組件掃描的起點。
b. 推斷 Web 應用類型
webApplicationType = WebApplicationType.deduceFromClasspath();
這一行代碼用于判斷當前是否為 Web 應用。它會檢查類路徑中是否存在 javax.servlet.Servlet
或 org.springframework.web.reactive.DispatcherHandler
等類,從而決定是否需要啟動 Web 容器(如 Tomcat)。
c. 加載 ApplicationContextInitializer
setInitializers((List) getSpringFactoriesInstances(ApplicationContextInitializer.class));
ApplicationContextInitializer
是 Spring 上下文刷新前的回調接口,用于在上下文創建之前執行一些初始化操作。Spring Boot 會從 META-INF/spring.factories
文件中讀取所有注冊的 ApplicationContextInitializer
實現類,并將其加入初始化列表。
d. 加載 ApplicationListener
setListeners((List) getSpringFactoriesInstances(ApplicationListener.class));
ApplicationListener
是 Spring 事件監聽機制的一部分,用于監聽 Spring 應用生命周期中的各種事件。Spring Boot 會在這里加載所有的 ApplicationListener
實現類,以便后續在啟動過程中觸發相應的事件通知。
e. 推斷主類
this.mainApplicationClass = deduceMainApplicationClass();
此步驟用于確定主類(即包含 main
方法的類),以便在后續的日志輸出和異常報告中使用。
3. run()
方法的執行流程
SpringApplication.run()
是 Spring Boot 啟動的核心方法,它負責創建并啟動 Spring 應用上下文。其大致執行流程如下:
(1)記錄啟動時間并觸發啟動事件
StopWatch stopWatch = new StopWatch();
stopWatch.start();ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
StopWatch
用于記錄啟動時間,方便性能分析。getRunListeners()
會從META-INF/spring.factories
中加載所有SpringApplicationRunListener
實現類,用于監聽啟動過程中的各個階段。listeners.starting()
觸發starting
事件,通知所有監聽者應用開始啟動。
(2)準備環境(Environment)
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
prepareEnvironment()
方法用于創建并配置Environment
對象,其中包括系統屬性、JVM 參數、application.properties
配置等。configureIgnoreBeanInfo()
用于優化 Java Beans Introspection 性能。
(3)打印 Banner
printBanner(environment);
- 如果用戶沒有禁用 Banner,Spring Boot 會在控制臺打印出經典的 ASCII 字符圖案,并顯示版本信息。
(4)創建 Spring 應用上下文
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
createApplicationContext()
會根據webApplicationType
的值選擇合適的ApplicationContext
實現類,例如:AnnotationConfigServletWebServerApplicationContext
(Web 應用)AnnotationConfigApplicationContext
(普通 Java 應用)
FailureAnalyzers
用于捕獲啟動失敗時的異常,并提供詳細的錯誤分析信息。
(5)準備上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
- 此步驟會將
Environment
、ApplicationArguments
、Banner
等對象注入到上下文中。 - 同時,還會執行所有注冊的
ApplicationContextInitializer
,并對上下文進行進一步的定制。
(6)刷新上下文
refreshContext(context);
refreshContext()
會調用 Spring 的AbstractApplicationContext.refresh()
方法,觸發 Spring 容器的初始化流程。- 在這個過程中,Spring 會加載所有的 Bean 定義,并完成自動裝配、依賴注入等核心操作。
(7)結束啟動流程
afterRefresh(context, applicationArguments);
stopWatch.stop();
listeners.finished(context, null);
afterRefresh()
會執行所有CommandLineRunner
和ApplicationRunner
接口的實現類,用于在應用啟動后執行自定義邏輯。stopWatch.stop()
記錄啟動耗時。listeners.finished()
通知所有監聽者應用啟動完成。
4. 小結
Spring Boot 的 main
方法雖然只有一行代碼,但它背后隱藏著一套高度封裝的啟動流程。通過 SpringApplication
類的構造和 run()
方法的執行,Spring Boot 會依次完成以下操作:
- 初始化 Spring Boot 的核心組件(如
ApplicationContextInitializer
、ApplicationListener
等)。 - 創建并配置
Environment
,加載外部配置。 - 打印 Banner 并創建 Spring 應用上下文。
- 準備上下文并執行自定義初始化邏輯。
- 刷新上下文,完成自動裝配和依賴注入。
- 啟動完成后執行
CommandLineRunner
或ApplicationRunner
。
理解 main
方法的執行流程有助于我們深入掌握 Spring Boot 的啟動機制,為后續的調試和優化打下基礎。接下來,我們將進一步探討 Spring Boot 啟動流程的整體架構,幫助你建立完整的認知體系。
Spring Boot 啟動流程概述
Spring Boot 的啟動流程是一套高度封裝且高度可擴展的機制,涵蓋了從應用啟動到上下文初始化、自動裝配、Web 服務器啟動等多個階段。理解整個啟動流程不僅有助于排查啟動問題,還能幫助開發者更好地利用 Spring Boot 的擴展機制進行自定義開發。
1. Spring Boot 啟動流程的整體架構
Spring Boot 的啟動流程可以劃分為以下幾個主要階段:
階段 | 描述 |
---|---|
1. 創建 SpringApplication 實例 | 初始化 Spring Boot 的核心組件,包括 ApplicationContextInitializer 、ApplicationListener 、推斷 Web 應用類型等 |
2. 觸發 starting 事件 | 通知所有監聽者應用即將啟動 |
3. 準備 Environment | 加載系統環境變量、JVM 參數、application.properties 等配置 |
4. 打印 Banner | 顯示 Spring Boot 的歡迎信息 |
5. 創建 ApplicationContext | 根據應用類型(Web 或非 Web)創建合適的上下文實例 |
6. 準備上下文 | 注入 Environment 、ApplicationArguments 、Banner 等,并執行 ApplicationContextInitializer |
7. 刷新上下文 | 觸發 Spring 容器的初始化流程,包括自動裝配、依賴注入等 |
8. 啟動完成后執行 CommandLineRunner 或 ApplicationRunner | 在應用啟動完成后執行自定義邏輯 |
9. 觸發 finished 事件 | 通知所有監聽者應用啟動完成 |
2. Spring Boot 啟動流程的關鍵組件
在整個啟動流程中,涉及多個核心組件,它們協同工作,共同完成 Spring Boot 的啟動過程。
(1)SpringApplication
類
SpringApplication
是 Spring Boot 啟動流程的核心類,負責引導整個應用的啟動。它提供了 run()
方法作為啟動入口,并在內部封裝了上下文創建、環境配置、事件監聽等關鍵操作。
(2)SpringApplicationRunListener
SpringApplicationRunListener
是 Spring Boot 啟動過程中的事件監聽器接口,用于監聽啟動的不同階段(如 starting
、environmentPrepared
、contextPrepared
、contextLoaded
、finished
等)。Spring Boot 默認提供了 EventPublishingRunListener
,它會將這些事件廣播給所有注冊的 ApplicationListener
。
(3)ApplicationContextInitializer
ApplicationContextInitializer
是 Spring 上下文初始化的回調接口,用于在上下文創建之后、刷新之前執行一些自定義的初始化邏輯。開發者可以通過實現該接口來自定義上下文的行為。
(4)ApplicationListener
ApplicationListener
是 Spring 事件監聽機制的一部分,用于監聽 Spring 應用生命周期中的各種事件。Spring Boot 在啟動過程中會發布多個事件,如 ApplicationStartingEvent
、ApplicationReadyEvent
、ApplicationFailedEvent
等,開發者可以通過監聽這些事件來執行自定義邏輯。
(5)Environment
Environment
是 Spring 應用的環境抽象,它包含了系統環境變量、JVM 參數、application.properties
配置等信息。Spring Boot 會在啟動過程中加載這些配置,并將其注入到上下文中。
(6)Banner
Banner
是 Spring Boot 啟動時打印的歡迎信息,默認情況下會在控制臺顯示 Spring Boot 的 ASCII 圖案。開發者可以通過自定義 banner.txt
文件或實現 Banner
接口來修改或禁用 Banner。
(7)CommandLineRunner
和 ApplicationRunner
這兩個接口用于在應用啟動完成后執行自定義邏輯。CommandLineRunner
接收原始的 String[] args
參數,而 ApplicationRunner
接收封裝后的 ApplicationArguments
對象。
(8)FailureAnalyzers
FailureAnalyzers
是 Spring Boot 提供的一種啟動失敗分析機制,它會在應用啟動失敗時收集異常信息,并提供詳細的錯誤分析報告,幫助開發者快速定位問題。
3. Spring Boot 啟動流程的執行順序
Spring Boot 的啟動流程可以細分為以下幾個步驟:
(1)初始化階段
- 創建
SpringApplication
實例 - 推斷 Web 應用類型
- 加載
ApplicationContextInitializer
- 加載
ApplicationListener
- 推斷主類(
mainApplicationClass
)
(2)啟動階段
- 觸發
starting
事件 - 準備
Environment
- 配置
ignoreBeanInfo
- 打印 Banner
(3)上下文創建階段
- 創建
ApplicationContext
- 創建
FailureAnalyzers
- 準備上下文(注入
Environment
、ApplicationArguments
、Banner
) - 執行
ApplicationContextInitializer
- 發布
contextPrepared
事件 - 加載應用的主類(
primarySource
)
(4)上下文刷新階段
- 刷新上下文(
refreshContext()
) - 觸發 Spring 容器的初始化流程(如自動裝配、依賴注入)
- 發布
applicationReadyEvent
事件
(5)啟動完成階段
- 執行
CommandLineRunner
和ApplicationRunner
- 停止
StopWatch
,記錄啟動耗時 - 觸發
finished
事件
4. Spring Boot 啟動流程的擴展點
Spring Boot 提供了多個擴展點,允許開發者在啟動過程中插入自定義邏輯。這些擴展點包括:
SpringApplicationRunListener
:監聽啟動的不同階段ApplicationContextInitializer
:在上下文創建后執行自定義初始化邏輯ApplicationListener
:監聽 Spring 應用生命周期中的各種事件CommandLineRunner
/ApplicationRunner
:在應用啟動完成后執行自定義邏輯
通過合理利用這些擴展點,開發者可以在不修改 Spring Boot 內部邏輯的情況下,實現高度定制化的啟動行為。
5. 小結
Spring Boot 的啟動流程是一個高度封裝且高度可擴展的機制,涵蓋了從應用啟動到上下文初始化、自動裝配、Web 服務器啟動等多個階段。通過理解整個啟動流程,開發者可以更好地掌握 Spring Boot 的運行機制,并利用其提供的擴展點進行自定義開發。接下來,我們將深入探討 SpringApplication
類的具體作用及其在啟動過程中的初始化流程。
SpringApplication 類的作用與初始化過程
SpringApplication
是 Spring Boot 啟動流程的核心類,它負責引導整個應用的啟動過程。雖然開發者通常只需要調用 SpringApplication.run()
方法即可啟動應用,但其內部封裝了大量邏輯,包括上下文初始化、環境配置、事件監聽等關鍵操作。理解 SpringApplication
的作用及其初始化過程,有助于我們更深入地掌握 Spring Boot 的啟動機制。
1. SpringApplication
的作用
SpringApplication
類的主要職責包括:
- 推斷應用類型:判斷當前應用是 Web 應用還是普通 Java 應用。
- 加載
ApplicationContextInitializer
:用于在上下文創建之前執行自定義初始化邏輯。 - 加載
ApplicationListener
:監聽 Spring Boot 啟動過程中的各種事件。 - 推斷主類(Main Application Class):確定包含
main
方法的類,以便在后續操作中使用。 - 創建
ApplicationContext
:根據應用類型(Web 或非 Web)創建合適的上下文實例。 - 觸發啟動事件:通知所有監聽者應用啟動的不同階段(如
starting
、environmentPrepared
、contextPrepared
、finished
等)。
2. SpringApplication
的構造過程
SpringApplication
的構造函數是啟動流程的第一步,它會初始化核心組件,并推斷應用的基本信息。其構造函數的源碼如下:
public SpringApplication(ResourceLoader resourceLoader, Class<?> primarySource) {this.resourceLoader = resourceLoader;Assert.notNull(primarySource, "Primary source must not be null");this.primarySources = new LinkedHashSet<>();this.primarySources.add(primarySource);this.webApplicationType = WebApplicationType.deduceFromClasspath();setInitializers((List) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((List) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();
}
讓我們逐一分析每個關鍵步驟。
(1)設置主類(Primary Source)
this.primarySources.add(primarySource);
主類(Primary Source)指的是包含 main
方法的類,即我們在調用 SpringApplication.run()
時傳入的類(如 DemoApplication.class
)。Spring Boot 會利用這個類來確定組件掃描的起點。
(2)推斷 Web 應用類型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
這一行代碼用于判斷當前是否為 Web 應用。它會檢查類路徑中是否存在 javax.servlet.Servlet
或 org.springframework.web.reactive.DispatcherHandler
等類,從而決定是否需要啟動 Web 容器(如 Tomcat)。
(3)加載 ApplicationContextInitializer
setInitializers((List) getSpringFactoriesInstances(ApplicationContextInitializer.class));
ApplicationContextInitializer
是 Spring 上下文初始化的回調接口,用于在上下文創建之后、刷新之前執行一些自定義的初始化邏輯。Spring Boot 會從 META-INF/spring.factories
文件中讀取所有注冊的 ApplicationContextInitializer
實現類,并將其加入初始化列表。
例如,在 spring-boot-autoconfigure
模塊的 META-INF/spring.factories
文件中,可能會包含如下配置:
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
(4)加載 ApplicationListener
setListeners((List) getSpringFactoriesInstances(ApplicationListener.class));
ApplicationListener
是 Spring 事件監聽機制的一部分,用于監聽 Spring 應用生命周期中的各種事件。Spring Boot 會在這里加載所有的 ApplicationListener
實現類,以便后續在啟動過程中觸發相應的事件通知。
例如,在 spring-boot-autoconfigure
模塊的 META-INF/spring.factories
文件中,可能會包含如下配置:
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
(5)推斷主類
this.mainApplicationClass = deduceMainApplicationClass();
此步驟用于確定主類(即包含 main
方法的類),以便在后續的日志輸出和異常報告中使用。
3. SpringApplication
的 run()
方法
在 SpringApplication
實例創建完成后,下一步是調用 run()
方法,啟動整個應用。其核心代碼如下:
public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;FailureAnalyzers analyzers = null;configureHeadlessProperty();SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting();try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);configureIgnoreBeanInfo(environment);printBanner(environment);context = createApplicationContext();analyzers = new FailureAnalyzers(context);prepareContext(context, environment, listeners, applicationArguments, printedBanner);refreshContext(context);afterRefresh(context, applicationArguments);listeners.finished(context, null);stopWatch.stop();return context;} catch (Throwable ex) {handleRunFailure(context, ex, analyzers, listeners);throw new IllegalStateException(ex);}
}
讓我們逐個分析 run()
方法中的關鍵步驟。
(1)記錄啟動時間并觸發 starting
事件
StopWatch stopWatch = new StopWatch();
stopWatch.start();SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
StopWatch
用于記錄啟動時間,方便性能分析。getRunListeners()
會從META-INF/spring.factories
中加載所有SpringApplicationRunListener
實現類,用于監聽啟動過程中的各個階段。listeners.starting()
觸發starting
事件,通知所有監聽者應用開始啟動。
(2)準備 Environment
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
prepareEnvironment()
方法用于創建并配置Environment
對象,其中包括系統屬性、JVM 參數、application.properties
配置等。configureIgnoreBeanInfo()
用于優化 Java Beans Introspection 性能。
(3)打印 Banner
printBanner(environment);
- 如果用戶沒有禁用 Banner,Spring Boot 會在控制臺打印出經典的 ASCII 字符圖案,并顯示版本信息。
(4)創建 ApplicationContext
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
createApplicationContext()
會根據webApplicationType
的值選擇合適的ApplicationContext
實現類,例如:AnnotationConfigServletWebServerApplicationContext
(Web 應用)AnnotationConfigApplicationContext
(普通 Java 應用)
FailureAnalyzers
用于捕獲啟動失敗時的異常,并提供詳細的錯誤分析信息。
(5)準備上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
- 此步驟會將
Environment
、ApplicationArguments
、Banner
等對象注入到上下文中。 - 同時,還會執行所有注冊的
ApplicationContextInitializer
,并對上下文進行進一步的定制。
(6)刷新上下文
refreshContext(context);
refreshContext()
會調用 Spring 的AbstractApplicationContext.refresh()
方法,觸發 Spring 容器的初始化流程。- 在這個過程中,Spring 會加載所有的 Bean 定義,并完成自動裝配、依賴注入等核心操作。
(7)啟動完成后執行 CommandLineRunner
或 ApplicationRunner
afterRefresh(context, applicationArguments);
afterRefresh()
會執行所有CommandLineRunner
和ApplicationRunner
接口的實現類,用于在應用啟動后執行自定義邏輯。
(8)結束啟動流程
listeners.finished(context, null);
stopWatch.stop();
listeners.finished()
通知所有監聽者應用啟動完成。stopWatch.stop()
記錄啟動耗時。
4. 小結
SpringApplication
是 Spring Boot 啟動流程的核心類,它負責引導整個應用的啟動過程。其構造函數會初始化核心組件(如 ApplicationContextInitializer
、ApplicationListener
、推斷 Web 應用類型等),并在 run()
方法中依次完成以下操作:
- 初始化 Spring Boot 的核心組件
- 創建并配置
Environment
- 打印 Banner 并創建 Spring 應用上下文
- 準備上下文并執行自定義初始化邏輯
- 刷新上下文,完成自動裝配和依賴注入
- 啟動完成后執行
CommandLineRunner
或ApplicationRunner