Spring Boot 啟動原理揭秘:從 main 方法到自動裝配

Spring Boot 啟動原理揭秘:從 main 方法到自動裝配

引言

Spring Boot 作為 Java 領域最流行的開發框架之一,憑借其“開箱即用”的特性極大地簡化了 Spring 應用的搭建和部署。然而,盡管開發者在日常工作中頻繁使用 Spring Boot 的啟動類(main 方法)和自動裝配機制,真正理解其底層原理的人卻并不多。本文將深入剖析 Spring Boot 的啟動流程,從 main 方法入手,逐步揭示 Spring Boot 是如何通過自動裝配實現零配置的奇跡的。

我們將按照以下結構展開:

  1. Spring Boot 簡介與核心優勢
  2. Spring Boot 應用的基本結構
  3. Spring Boot 的 main 方法分析
  4. Spring Boot 啟動流程概述
  5. SpringApplication 類的作用與初始化過程
  6. Spring Boot 的上下文環境(ApplicationContext)創建過程
  7. Spring Boot 的自動裝配機制詳解
  8. Spring Boot 的條件化裝配(Conditional On)機制
  9. Spring Boot 啟動過程中的事件監聽機制
  10. Spring Boot 啟動過程中常見的擴展點與自定義配置
  11. 總結與最佳實踐

接下來,我們將逐一解析這些內容,幫助你全面掌握 Spring Boot 的啟動原理。

Spring Boot 簡介與核心優勢

什么是 Spring Boot?

Spring Boot 是由 Pivotal 團隊推出的一個開源框架,旨在簡化 Spring 應用的初始搭建和開發。它基于 Spring Framework 構建,提供了一種快速、便捷的方式來構建獨立運行的、生產級的應用程序。Spring Boot 的核心理念是“約定優于配置”,這意味著開發者無需手動編寫大量 XML 或 Java 配置代碼即可快速啟動項目。

Spring Boot 的核心優勢

  1. 開箱即用(Opinionated Starter Dependencies)
    Spring Boot 提供了一系列預配置的依賴項(稱為 “Starter”),例如 spring-boot-starter-webspring-boot-starter-data-jpa 等。這些依賴項已經集成了常用的庫和默認配置,開發者只需引入對應的 Starter 即可直接使用相關功能,而無需手動配置復雜的依賴關系。

  2. 內嵌服務器(Embedded Server)
    Spring Boot 默認支持內嵌的 Tomcat、Jetty 或 Undertow 服務器,這意味著開發者可以將應用程序打包為一個獨立的 JAR 文件,并直接運行,而無需額外部署到外部應用服務器。這大大簡化了部署流程,提高了開發效率。

  3. 自動裝配(Auto-Configuration)
    Spring Boot 最具代表性的特性之一就是自動裝配。它能夠根據類路徑上的依賴項自動推斷所需的配置,并注冊相應的 Bean。例如,如果項目中包含 H2 數據庫驅動,Spring Boot 會自動配置內存數據庫連接池和相關的 DAO 組件,而無需開發者手動編寫 DataSource 配置。

  4. 零配置(Zero Configuration)
    Spring Boot 結合自動裝配和默認約定,使得開發者幾乎不需要編寫任何 XML 或 Java 配置文件即可啟動應用。這種零配置的理念極大降低了學習成本,提高了開發效率。

  5. 生產就緒(Production Ready)
    Spring Boot 提供了許多生產級別的功能,如健康檢查(Health Check)、指標監控(Metrics)、日志管理(Log Management)等。這些功能可以幫助開發者更好地監控和維護應用程序。

  6. 強大的 CLI 工具(Command Line Interface)
    Spring Boot 提供了一個命令行工具(CLI),允許開發者通過簡單的命令快速創建原型應用,甚至可以直接運行 Groovy 腳本而無需編譯。

  7. 豐富的文檔和社區支持
    Spring Boot 擁有龐大的社區和詳盡的官方文檔,開發者可以輕松找到解決方案或最佳實踐。此外,Spring Boot 還與許多其他流行框架(如 Spring Cloud、Spring Security、Spring Data 等)無縫集成,形成了完整的生態系統。

  8. 靈活的配置方式
    Spring Boot 支持多種配置方式,包括 application.propertiesapplication.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.propertiesapplication.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 會經歷以下幾個主要階段:

  1. 加載 Spring Boot 的自動裝配機制:根據類路徑上的依賴項,自動配置各類組件。
  2. 創建 Spring 應用上下文(ApplicationContext):初始化 IoC 容器,注冊 Bean。
  3. 啟動內嵌 Web 服務器(如 Tomcat):如果是 Web 應用,則啟動 Web 服務器并監聽指定端口。
  4. 執行 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.Servletorg.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);
  • 此步驟會將 EnvironmentApplicationArgumentsBanner 等對象注入到上下文中。
  • 同時,還會執行所有注冊的 ApplicationContextInitializer,并對上下文進行進一步的定制。
(6)刷新上下文
refreshContext(context);
  • refreshContext() 會調用 Spring 的 AbstractApplicationContext.refresh() 方法,觸發 Spring 容器的初始化流程。
  • 在這個過程中,Spring 會加載所有的 Bean 定義,并完成自動裝配、依賴注入等核心操作。
(7)結束啟動流程
afterRefresh(context, applicationArguments);
stopWatch.stop();
listeners.finished(context, null);
  • afterRefresh() 會執行所有 CommandLineRunnerApplicationRunner 接口的實現類,用于在應用啟動后執行自定義邏輯。
  • stopWatch.stop() 記錄啟動耗時。
  • listeners.finished() 通知所有監聽者應用啟動完成。

4. 小結

Spring Boot 的 main 方法雖然只有一行代碼,但它背后隱藏著一套高度封裝的啟動流程。通過 SpringApplication 類的構造和 run() 方法的執行,Spring Boot 會依次完成以下操作:

  1. 初始化 Spring Boot 的核心組件(如 ApplicationContextInitializerApplicationListener 等)。
  2. 創建并配置 Environment,加載外部配置。
  3. 打印 Banner 并創建 Spring 應用上下文。
  4. 準備上下文并執行自定義初始化邏輯。
  5. 刷新上下文,完成自動裝配和依賴注入。
  6. 啟動完成后執行 CommandLineRunnerApplicationRunner

理解 main 方法的執行流程有助于我們深入掌握 Spring Boot 的啟動機制,為后續的調試和優化打下基礎。接下來,我們將進一步探討 Spring Boot 啟動流程的整體架構,幫助你建立完整的認知體系。

Spring Boot 啟動流程概述

Spring Boot 的啟動流程是一套高度封裝且高度可擴展的機制,涵蓋了從應用啟動到上下文初始化、自動裝配、Web 服務器啟動等多個階段。理解整個啟動流程不僅有助于排查啟動問題,還能幫助開發者更好地利用 Spring Boot 的擴展機制進行自定義開發。

1. Spring Boot 啟動流程的整體架構

Spring Boot 的啟動流程可以劃分為以下幾個主要階段:

階段描述
1. 創建 SpringApplication 實例初始化 Spring Boot 的核心組件,包括 ApplicationContextInitializerApplicationListener、推斷 Web 應用類型等
2. 觸發 starting 事件通知所有監聽者應用即將啟動
3. 準備 Environment加載系統環境變量、JVM 參數、application.properties 等配置
4. 打印 Banner顯示 Spring Boot 的歡迎信息
5. 創建 ApplicationContext根據應用類型(Web 或非 Web)創建合適的上下文實例
6. 準備上下文注入 EnvironmentApplicationArgumentsBanner 等,并執行 ApplicationContextInitializer
7. 刷新上下文觸發 Spring 容器的初始化流程,包括自動裝配、依賴注入等
8. 啟動完成后執行 CommandLineRunnerApplicationRunner在應用啟動完成后執行自定義邏輯
9. 觸發 finished 事件通知所有監聽者應用啟動完成

2. Spring Boot 啟動流程的關鍵組件

在整個啟動流程中,涉及多個核心組件,它們協同工作,共同完成 Spring Boot 的啟動過程。

(1)SpringApplication

SpringApplication 是 Spring Boot 啟動流程的核心類,負責引導整個應用的啟動。它提供了 run() 方法作為啟動入口,并在內部封裝了上下文創建、環境配置、事件監聽等關鍵操作。

(2)SpringApplicationRunListener

SpringApplicationRunListener 是 Spring Boot 啟動過程中的事件監聽器接口,用于監聽啟動的不同階段(如 startingenvironmentPreparedcontextPreparedcontextLoadedfinished 等)。Spring Boot 默認提供了 EventPublishingRunListener,它會將這些事件廣播給所有注冊的 ApplicationListener

(3)ApplicationContextInitializer

ApplicationContextInitializer 是 Spring 上下文初始化的回調接口,用于在上下文創建之后、刷新之前執行一些自定義的初始化邏輯。開發者可以通過實現該接口來自定義上下文的行為。

(4)ApplicationListener

ApplicationListener 是 Spring 事件監聽機制的一部分,用于監聽 Spring 應用生命周期中的各種事件。Spring Boot 在啟動過程中會發布多個事件,如 ApplicationStartingEventApplicationReadyEventApplicationFailedEvent 等,開發者可以通過監聽這些事件來執行自定義邏輯。

(5)Environment

Environment 是 Spring 應用的環境抽象,它包含了系統環境變量、JVM 參數、application.properties 配置等信息。Spring Boot 會在啟動過程中加載這些配置,并將其注入到上下文中。

(6)Banner

Banner 是 Spring Boot 啟動時打印的歡迎信息,默認情況下會在控制臺顯示 Spring Boot 的 ASCII 圖案。開發者可以通過自定義 banner.txt 文件或實現 Banner 接口來修改或禁用 Banner。

(7)CommandLineRunnerApplicationRunner

這兩個接口用于在應用啟動完成后執行自定義邏輯。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
  • 準備上下文(注入 EnvironmentApplicationArgumentsBanner
  • 執行 ApplicationContextInitializer
  • 發布 contextPrepared 事件
  • 加載應用的主類(primarySource
(4)上下文刷新階段
  • 刷新上下文(refreshContext()
  • 觸發 Spring 容器的初始化流程(如自動裝配、依賴注入)
  • 發布 applicationReadyEvent 事件
(5)啟動完成階段
  • 執行 CommandLineRunnerApplicationRunner
  • 停止 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 類的主要職責包括:

  1. 推斷應用類型:判斷當前應用是 Web 應用還是普通 Java 應用。
  2. 加載 ApplicationContextInitializer:用于在上下文創建之前執行自定義初始化邏輯。
  3. 加載 ApplicationListener:監聽 Spring Boot 啟動過程中的各種事件。
  4. 推斷主類(Main Application Class):確定包含 main 方法的類,以便在后續操作中使用。
  5. 創建 ApplicationContext:根據應用類型(Web 或非 Web)創建合適的上下文實例。
  6. 觸發啟動事件:通知所有監聽者應用啟動的不同階段(如 startingenvironmentPreparedcontextPreparedfinished 等)。

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.Servletorg.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. SpringApplicationrun() 方法

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);
  • 此步驟會將 EnvironmentApplicationArgumentsBanner 等對象注入到上下文中。
  • 同時,還會執行所有注冊的 ApplicationContextInitializer,并對上下文進行進一步的定制。
(6)刷新上下文
refreshContext(context);
  • refreshContext() 會調用 Spring 的 AbstractApplicationContext.refresh() 方法,觸發 Spring 容器的初始化流程。
  • 在這個過程中,Spring 會加載所有的 Bean 定義,并完成自動裝配、依賴注入等核心操作。
(7)啟動完成后執行 CommandLineRunnerApplicationRunner
afterRefresh(context, applicationArguments);
  • afterRefresh() 會執行所有 CommandLineRunnerApplicationRunner 接口的實現類,用于在應用啟動后執行自定義邏輯。
(8)結束啟動流程
listeners.finished(context, null);
stopWatch.stop();
  • listeners.finished() 通知所有監聽者應用啟動完成。
  • stopWatch.stop() 記錄啟動耗時。

4. 小結

SpringApplication 是 Spring Boot 啟動流程的核心類,它負責引導整個應用的啟動過程。其構造函數會初始化核心組件(如 ApplicationContextInitializerApplicationListener、推斷 Web 應用類型等),并在 run() 方法中依次完成以下操作:

  1. 初始化 Spring Boot 的核心組件
  2. 創建并配置 Environment
  3. 打印 Banner 并創建 Spring 應用上下文
  4. 準備上下文并執行自定義初始化邏輯
  5. 刷新上下文,完成自動裝配和依賴注入
  6. 啟動完成后執行 CommandLineRunnerApplicationRunner

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/88994.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/88994.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/88994.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

OpenCV 與深度學習:從圖像分類到目標檢測技術

一、深度學習&#xff1a;從 “人工設計” 到 “自動學習”1.1 深度學習的定位&#xff1a;AI、機器學習與深度學習的關系人工智能&#xff08;AI&#xff09;&#xff1a;是一個寬泛的領域&#xff0c;目標是構建能模擬人類智能的系統&#xff0c;涵蓋推理、感知、決策等能力。…

Docker 鏡像推送至 Coding 制品倉庫超時問題排查與解決

Docker 鏡像推送至 Coding 制品倉庫超時問題排查與解決 在將 Docker 鏡像推送至 Coding 的制品倉庫時&#xff0c;可能會遇到 docker push 命令超時失敗的問題。但使用 curl -i http://xxx.coding.xxxx.xx 測試時&#xff0c;連接卻能成功建立。以下是排查過程及解決方案。 問題…

https交互原理

Https 交互時序圖&#xff1a;HTTPS 通信中結合 RSA 和 AES 加密的流程&#xff0c;本質是利用 RSA 的安全特性交換 AES 密鑰&#xff0c;再用高效的 AES 加密實際數據傳輸。HTTPS 交互核心流程&#xff08;TLS/SSL 握手&#xff09; 1. 建立 TCP 連接 客戶端通過 TCP 三次握手…

LSTM入門案例(時間序列預測)| pytorch實現

需求 假如我有一個時間序列&#xff0c;例如是前113天的價格數據&#xff08;訓練集&#xff09;&#xff0c;然后我希望借此預測后30天的數據&#xff08;測試集&#xff09;&#xff0c;實際上這143天的價格數據都已經有了。這里為了簡單&#xff0c;每一天的數據只有一個價…

WPS、Word加載項開發流程(免費最簡版本)

文章目錄1 加載項對比2 WPS 加載項2.1 本地開發2.1.1 準備開發環境2.1.2 新建 WPS 加載項項目2.1.3 運行項目2.2 在線部署2.2.1 編譯項目2.2.2 部署項目2.2.3 生成分發文件2.2.4 部署分發文件2.3 安裝加載項2.4 取消發布3 Word 加載項3.1 本地開發3.1.1 準備開發環境3.1.2 新建…

Flink SQL 性能優化實戰

最近我們組在大規模上線Flink SQL作業。首先&#xff0c;在進行跑批量初始化完歷史數據后&#xff0c;剩下的就是消費Kafka歷史數據進行追數了。但是發現某些作業的追數過程十分緩慢&#xff0c;要運行一晚上甚至三四天才能追上最新數據。由于是實時數倉指標計算上線初期&#…

HTML 樹結構(DOM)深入講解教程

一、HTML 樹結構的核心概念 1.1 DOM&#xff08;文檔對象模型&#xff09;的定義 DOM&#xff08;Document Object Model&#xff09;是 W3C 制定的標準接口&#xff0c;允許程序或腳本&#xff08;如 JavaScript&#xff09;動態訪問和更新 HTML/XML 文檔的內容、結構和樣式。…

用鼠標點擊終端窗口的時候出現:0;61;50M0;61;50M0;62;50M0

在做aws webrtc viewer拉流壓測的過程中&#xff0c;我本地打開了多個終端&#xff0c;用于連接EC2實例&#xff1a; 一個終端用于啟動 ‘并發master腳本’、監控master端的cpu、mem&#xff1b;一個終端用于監控master端的帶寬情況&#xff1b;一個終端用于監控viewer端的cpu、…

C++-linux 5.gdb調試工具

GDB調試工具 在C/C開發中&#xff0c;程序運行時的錯誤往往比編譯錯誤更難定位。GDB&#xff08;GNU Debugger&#xff09;是Linux環境下最強大的程序調試工具&#xff0c;能夠幫助開發者追蹤程序執行流程、查看變量狀態、定位內存錯誤等。本章將從基礎到進階&#xff0c;全面講…

Update~Read PLC for Chart ~ Log By Shift To be... Alarm AI Machine Learning

上圖~ 持續迭代 1、增加報警彈窗,具體到哪個值,雙邊規格具體是多少 2、實時顯示當前值的統計特征,Max Min AVG ... import tkinter as tk from tkinter import simpledialog import time import threading import queue import logging from datetime import datet…

es的自定義詞典和停用詞

在 Elasticsearch 中&#xff0c;自定義詞典是優化分詞效果的核心手段&#xff0c;尤其適用于中文或專業領域的文本處理。以下是關于 ES 自定義詞典的完整指南&#xff1a; 為什么需要自定義詞典&#xff1f; 默認分詞不足&#xff1a; ES 自帶的分詞器&#xff08;如 Standard…

微算法科技技術突破:用于前饋神經網絡的量子算法技術助力神經網絡變革

隨著量子計算和機器學習的迅猛發展&#xff0c;企業界正逐步邁向融合這兩大領域的新時代。在這一背景下&#xff0c;微算法科技&#xff08;NASDAQ:MLGO&#xff09;成功研發出一套用于前饋神經網絡的量子算法&#xff0c;突破了傳統神經網絡在訓練和評估中的性能瓶頸。這一創新…

一文讀懂循環神經網絡(RNN)—語言模型+讀取長序列數據(2)

目錄 讀取長序列數據 為什么需要 “讀取長序列數據”&#xff1f; 讀取長序列數據的核心方法 1. 滑動窗口&#xff08;Sliding Window&#xff09; 2. 分段截取&#xff08;Segmentation&#xff09; 3. 滾動生成&#xff08;Rolling Generation&#xff09; 4. 關鍵信息…

Oracle Virtualbox 虛擬機配置靜態IP

Oracle Virtualbox 虛擬機配置靜態IP VirtualBox的網卡&#xff0c;默認都是第一個不能自定義&#xff0c;后續新建的可以自定義。 新建NAT網卡、host主機模式網卡 依次點擊&#xff1a;管理->工具->網絡管理器新建host主機模式網卡 這個網卡的網段自定義&#xff0c;創建…

Linux RAID1 創建與配置實戰指南(mdadm)

Linux RAID1 創建與配置實戰指南&#xff08;mdadm&#xff09;一、RAID1 核心價值與實戰目標RAID1&#xff08;磁盤鏡像&#xff09; 通過數據冗余提供高可靠性&#xff1a;當單塊硬盤損壞時&#xff0c;數據不丟失支持快速陣列重建讀寫性能略低于單盤&#xff08;鏡像寫入開銷…

MySQL數據庫----函數

目錄函數1&#xff0c;字符串函數2&#xff0c;數值函數3&#xff0c;日期函數4&#xff0c;流程函數函數 1&#xff0c;字符串函數 MySQL中內置了很多字符串函數 2&#xff0c;數值函數 3&#xff0c;日期函數 4&#xff0c;流程函數

1.2 vue2(組合式API)的語法結構以及外部暴露

vue2 vue3中可以寫vue2的語法&#xff0c;vue2的結構像一個花盆里的根&#xff08;根組件App.vue&#xff09;&#xff0c;根上可以插上不同的枝杈和花朵&#xff08;組件&#xff09;。 組件的結構&#xff1a; // 這里寫邏輯行為 <script lang"ts"> export d…

Swift 解 LeetCode 324:一步步實現擺動排序 II,掌握數組重排的節奏感

文章目錄摘要描述題解答案題解代碼&#xff08;Swift&#xff09;題解代碼分析步驟一&#xff1a;排序數組步驟二&#xff1a;左右指針分段步驟三&#xff1a;按位置交錯插入示例測試及結果示例 1示例 2示例 3&#xff08;邊界情況&#xff09;時間復雜度分析空間復雜度分析總結…

使用SQLMAP的文章管理系統CMS的sql注入滲透測試

SQLMAP注入演示&#xff1a;抓包拿到Cookie:召喚sqlmap&#xff1a;sqlmap -u "http://192.168.1.99:8085/show.php?id34" --cookie "pma_langzh_CN; kbqug_admin_username2621-PL_LxhFjyVe43ZuQvht6MI5q0ZcpRVV5FI0pzQ6XR8; kbqug_siteid2621-PL_LxhFjyVe4yA5…

I3C通信協議核心詳解

一、物理層與電氣特性雙線結構 SCL&#xff08;串行時鐘線&#xff09;&#xff1a;主設備控制&#xff0c;支持 推挽&#xff08;Push-Pull&#xff09;輸出&#xff08;高速模式&#xff09;和 開漏&#xff08;Open-Drain&#xff09;&#xff08;兼容I2C模式&#xff09;。…