Spring Boot啟動(dòng)流程示例詳解
1. Spring Boot啟動(dòng)流程概述
1.1 Spring Boot的核心特性
Spring Boot是Spring官方推出的一個(gè)快速構(gòu)建獨(dú)立運(yùn)行、生產(chǎn)級(jí)別Spring應(yīng)用的框架,它的核心目標(biāo)是簡化Spring應(yīng)用的搭建與部署。
核心特性主要包括:
- 內(nèi)嵌Web容器
- 內(nèi)置Tomcat、Jetty、Undertow,無需單獨(dú)部署WAR包到外部容器。只需運(yùn)行
java -jar即可啟動(dòng)。 - 自動(dòng)配置(Auto Configuration)
- 通過
@EnableAutoConfiguration結(jié)合條件化裝配(Conditional注解)自動(dòng)為應(yīng)用注冊(cè)Bean,大幅減少xml或Java配置的編寫量。 - 獨(dú)立運(yùn)行(Standalone)
- 應(yīng)用可以以一個(gè)獨(dú)立的JAR包形式運(yùn)行,避免復(fù)雜的服務(wù)器配置。
- 起步依賴(Starter Dependencies)
- 提供
spring-boot-starter-*依賴包,按需引入常用功能,減少手動(dòng)管理依賴的復(fù)雜度。 - 生產(chǎn)環(huán)境特性
- 提供
Actuator監(jiān)控、健康檢查、環(huán)境信息、度量指標(biāo)等。
類比:可以把Spring Boot想象成“開箱即用的開發(fā)平臺(tái)”,你不需要自己組裝各種組件,它已經(jīng)幫你準(zhǔn)備好了一整套“工具箱”和“默認(rèn)配置”。
1.2 啟動(dòng)流程整體架構(gòu)(文字描述)
Spring Boot啟動(dòng)的核心流程可以抽象成五個(gè)階段:
- 初始化SpringApplication
- 解析啟動(dòng)類(
@SpringBootApplication標(biāo)注的類) - 推斷應(yīng)用類型(
WebApplicationType) - 加載初始化器(
ApplicationContextInitializer) - 加載監(jiān)聽器(
ApplicationListener)
- 解析啟動(dòng)類(
- 準(zhǔn)備運(yùn)行環(huán)境(Environment)
- 創(chuàng)建并配置
Environment對(duì)象 - 加載外部配置(命令行參數(shù)、配置文件、系統(tǒng)屬性等)
- 配置轉(zhuǎn)換器(
PropertySource->Environment)
- 創(chuàng)建并配置
- 創(chuàng)建并刷新ApplicationContext
- 根據(jù)應(yīng)用類型創(chuàng)建合適的
ApplicationContext(Servlet、Reactive或普通應(yīng)用) - 注冊(cè)Bean定義、執(zhí)行
BeanFactoryPostProcessor和BeanPostProcessor - 啟動(dòng)內(nèi)嵌Web容器(如Tomcat)
- 根據(jù)應(yīng)用類型創(chuàng)建合適的
- 自動(dòng)配置與Bean加載
- 解析
@EnableAutoConfiguration加載自動(dòng)配置類 - 條件化裝配判斷是否加載特定Bean
- 初始化所有單例Bean
- 解析
- 完成啟動(dòng)并執(zhí)行回調(diào)
- 發(fā)布
ApplicationReadyEvent事件 - 執(zhí)行
CommandLineRunner和ApplicationRunner
- 發(fā)布
文字版的啟動(dòng)時(shí)序:
SpringApplication實(shí)例化 -> 推斷Web類型 -> 加載監(jiān)聽器 -> 創(chuàng)建Environment -> 加載配置 -> 創(chuàng)建ApplicationContext -> 自動(dòng)配置 -> 啟動(dòng)容器 -> 應(yīng)用就緒
1.3 Spring Boot 2.x 與 3.x 啟動(dòng)機(jī)制差異
在Spring Boot 3.x(基于Spring Framework 6)中,啟動(dòng)流程的總體框架與2.x一致,但有一些細(xì)節(jié)變化:
- WebApplicationType推斷優(yōu)化
- Spring Boot 3.x在推斷應(yīng)用類型時(shí),對(duì)
org.springframework.web.reactive.DispatcherHandler等類的檢測(cè)進(jìn)行了微調(diào),更精確地區(qū)分Servlet與Reactive應(yīng)用。
- Spring Boot 3.x在推斷應(yīng)用類型時(shí),對(duì)
- SpringFactoriesLoader升級(jí)
- 2.x版本讀取
META-INF/spring.factories文件使用Properties加載,3.x版本引入了新的API(SpringFactoriesLoader.forDefaultResourceLocation()),并且支持META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,提高了加載性能。
- 2.x版本讀取
- BootstrapContext增強(qiáng)
- 3.x版本中
BootstrapContext在啟動(dòng)階段的使用更靈活,可以在應(yīng)用啟動(dòng)前注冊(cè)額外的組件。
- 3.x版本中
- Jakarta命名空間遷移
- 所有與Servlet相關(guān)的類名由
javax.*遷移到jakarta.*,這在3.x升級(jí)時(shí)需要特別注意。
- 所有與Servlet相關(guān)的類名由
示例:最簡單的Spring Boot引導(dǎo)類
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @SpringBootApplication 是三個(gè)注解的組合:
* - @Configuration:表示該類是一個(gè)配置類
* - @EnableAutoConfiguration:開啟自動(dòng)配置
* - @ComponentScan:開啟包掃描
*/
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
// run方法會(huì)啟動(dòng)整個(gè)Spring Boot應(yīng)用
SpringApplication.run(DemoApplication.class, args);
}
}運(yùn)行上面的程序,你會(huì)在控制臺(tái)看到類似輸出(簡化版):
:: Spring Boot :: (v3.2.0)
2025-08-11 10:15:00 INFO --- Starting DemoApplication on localhost with PID 12345
2025-08-11 10:15:01 INFO --- Tomcat started on port(s): 8080 (http)
2025-08-11 10:15:01 INFO --- Started DemoApplication in 1.234 seconds
2. Spring Boot啟動(dòng)的核心階段
2.1 SpringApplication初始化階段
當(dāng)你調(diào)用:
SpringApplication.run(DemoApplication.class, args);
實(shí)際上會(huì)先執(zhí)行 new SpringApplication(primarySources...) 構(gòu)造方法,再執(zhí)行 .run(args) 方法。
源碼入口(Spring Boot 2.x / 3.x)
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 推斷應(yīng)用類型(Servlet / Reactive / None)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 從 spring.factories 加載 ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 從 spring.factories 加載 ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推斷并設(shè)置 main 方法所在類
this.mainApplicationClass = deduceMainApplicationClass();
}幾個(gè)關(guān)鍵點(diǎn):
- 推斷 WebApplicationType
- 如果classpath中存在
DispatcherServlet,就認(rèn)為是Servlet類型應(yīng)用; - 如果存在
DispatcherHandler,就認(rèn)為是Reactive類型應(yīng)用; - 否則是普通的
NONE類型(無Web環(huán)境)。
- 如果classpath中存在
- 加載初始化器(Initializers)
- 通過
SpringFactoriesLoader掃描META-INF/spring.factories,自動(dòng)實(shí)例化所有ApplicationContextInitializer實(shí)現(xiàn)類。
- 通過
- 加載監(jiān)聽器(Listeners)
- 同樣用
SpringFactoriesLoader機(jī)制加載ApplicationListener實(shí)現(xiàn)類,用于監(jiān)聽?wèi)?yīng)用啟動(dòng)過程的事件。
- 同樣用
- 推斷Main類
- 遍歷當(dāng)前線程的調(diào)用棧,找到第一個(gè)包含
main方法的類。
- 遍歷當(dāng)前線程的調(diào)用棧,找到第一個(gè)包含
示例:自定義ApplicationContextInitializer
public class MyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext context) {
System.out.println("MyInitializer: 應(yīng)用上下文初始化中...");
}
}
resources/META-INF/spring.factories:
org.springframework.context.ApplicationContextInitializer=\ com.example.demo.MyInitializer
啟動(dòng)時(shí)會(huì)先輸出:
MyInitializer: 應(yīng)用上下文初始化中...
說明在SpringApplication構(gòu)造時(shí)已成功加載該初始化器。
2.2 SpringApplication.run()執(zhí)行過程全解析
SpringApplication.run() 是整個(gè)啟動(dòng)的核心,下面是簡化后的源碼執(zhí)行順序(以3.x為例):
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 啟動(dòng)監(jiān)聽器集合
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
// 初始化引導(dǎo)上下文(BootstrapContext)
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
// 創(chuàng)建并配置 Environment
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, args);
// 打印 Banner
Banner printedBanner = printBanner(environment);
// 創(chuàng)建 ApplicationContext
context = createApplicationContext();
// 準(zhǔn)備上下文
prepareContext(bootstrapContext, context, environment, listeners, args, printedBanner);
// 刷新上下文(加載Bean、啟動(dòng)內(nèi)嵌容器)
refreshContext(context);
// 執(zhí)行 CommandLineRunner、ApplicationRunner
afterRefresh(context, args);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
listeners.running(context);
return context;
}分步驟解析:
- StopWatch計(jì)時(shí)器
- 用于記錄啟動(dòng)耗時(shí),最終會(huì)在日志輸出
Started Xxx in 2.345 seconds。
- 用于記錄啟動(dòng)耗時(shí),最終會(huì)在日志輸出
- 啟動(dòng)監(jiān)聽器(SpringApplicationRunListeners)
- 觸發(fā)
ApplicationStartingEvent事件,可以在這里做日志或系統(tǒng)初始化。
- 觸發(fā)
- 創(chuàng)建BootstrapContext
- 啟動(dòng)早期用于注冊(cè)額外組件,如配置加載器(Spring Cloud 會(huì)用到)。
- 創(chuàng)建并準(zhǔn)備Environment
- 加載外部配置(命令行、配置文件、環(huán)境變量)。
- 設(shè)置
PropertySource的加載順序。
- 打印Banner
- 控制臺(tái)輸出Spring Boot ASCII藝術(shù)字,可以自定義。
- 創(chuàng)建ApplicationContext
- 根據(jù)Web類型創(chuàng)建
AnnotationConfigServletWebServerApplicationContext或AnnotationConfigApplicationContext。
- 根據(jù)Web類型創(chuàng)建
- prepareContext
- 注冊(cè)初始化器(Initializer)。
- 注冊(cè)監(jiān)聽器(Listener)。
- 注冊(cè)主配置類(啟動(dòng)類)。
- refreshContext
- 調(diào)用Spring核心的
refresh()方法,完成Bean的加載、依賴注入、事件注冊(cè)等。
- 調(diào)用Spring核心的
- 執(zhí)行Runner回調(diào)
CommandLineRunner和ApplicationRunner用于啟動(dòng)完成后的業(yè)務(wù)處理。
- 觸發(fā)應(yīng)用就緒事件
- 發(fā)布
ApplicationReadyEvent,代表應(yīng)用完全啟動(dòng)并可對(duì)外提供服務(wù)。
- 發(fā)布
示例:監(jiān)聽啟動(dòng)事件
@Component
public class MyStartupListener implements ApplicationListener<ApplicationStartingEvent> {
@Override
public void onApplicationEvent(ApplicationStartingEvent event) {
System.out.println("應(yīng)用啟動(dòng)中:執(zhí)行早期初始化邏輯...");
}
}2.3 WebApplicationType推斷機(jī)制
源碼(簡化):
public static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(REACTIVE_DISPATCHER, null) && !ClassUtils.isPresent(DISPATCHER_SERVLET, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}判斷邏輯:
- 如果存在
org.springframework.web.reactive.DispatcherHandler且不存在DispatcherServlet→ REACTIVE - 如果缺少Servlet必要類 → NONE
- 其他情況默認(rèn)SERVLET
?? 小結(jié):
SpringApplication構(gòu)造階段主要是推斷類型、加載初始化器、監(jiān)聽器。run()方法則是準(zhǔn)備環(huán)境、創(chuàng)建上下文、刷新Bean、執(zhí)行回調(diào)的全過程。- Web類型推斷保證了Spring Boot能自動(dòng)適配不同類型的應(yīng)用(Servlet、Reactive、None)。
3. 環(huán)境準(zhǔn)備與配置加載
3.1 Environment對(duì)象的創(chuàng)建與類型
在 Spring Boot 啟動(dòng)時(shí),會(huì)根據(jù)應(yīng)用類型(WebApplicationType)來創(chuàng)建不同的 Environment 實(shí)現(xiàn)類:
| 應(yīng)用類型 | Environment實(shí)現(xiàn)類 |
|---|---|
| SERVLET | StandardServletEnvironment |
| REACTIVE | StandardReactiveWebEnvironment |
| NONE | StandardEnvironment |
源碼位置(SpringApplication.prepareEnvironment):
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}Environment 作用:
- 存放應(yīng)用的配置源(
PropertySource)。 - 提供配置值的統(tǒng)一獲取接口(
getProperty)。 - 提供 profile 管理(
setActiveProfiles)。
小示例:獲取Environment對(duì)象
@Component
public class EnvPrinter implements CommandLineRunner {
@Autowired
private Environment environment;
@Override
public void run(String... args) {
System.out.println("當(dāng)前激活的profile: " + Arrays.toString(environment.getActiveProfiles()));
System.out.println("server.port=" + environment.getProperty("server.port"));
}
}運(yùn)行時(shí)會(huì)輸出配置文件或命令行參數(shù)中對(duì)應(yīng)的值。
3.2 配置源加載順序與優(yōu)先級(jí)
Spring Boot 的配置加載是有優(yōu)先級(jí)順序的,后加載的會(huì)覆蓋先加載的。
官方的 優(yōu)先級(jí)順序(從高到低):
- 命令行參數(shù)
--server.port=9000
- Java系統(tǒng)屬性
System.setProperty("server.port", "9000")
- 操作系統(tǒng)環(huán)境變量
application-{profile}.properties(當(dāng)前激活的 profile 配置)application.properties/application.yml
- 打包在 jar 外部的配置文件(優(yōu)先于 jar 內(nèi)部配置文件)
- 打包在 jar 內(nèi)部的配置文件
- 默認(rèn)配置(代碼中
SpringApplication.setDefaultProperties()設(shè)置)
- 默認(rèn)配置(代碼中
命令行參數(shù)覆蓋示例
假設(shè) application.properties 中配置:
server.port=8080
運(yùn)行:
java -jar demo.jar --server.port=9001
最終生效端口是 9001,因?yàn)槊钚袇?shù)優(yōu)先級(jí)最高。
3.3 application.properties 與 application.yml 加載規(guī)則
Spring Boot 支持 .properties 和 .yml 兩種格式,加載順序(同一位置)是:
- 先加載
.properties - 再加載
.yml
并且,配置文件可以放在多個(gè)位置:
classpath:/classpath:/config/file:./file:./config/
優(yōu)先級(jí)順序(從高到低):
file:./config/file:./classpath:/config/classpath:/
3.4 命令行參數(shù)、系統(tǒng)屬性覆蓋規(guī)則
Spring Boot 會(huì)自動(dòng)將命令行參數(shù)解析成 PropertySource 并加入 Environment,它們的優(yōu)先級(jí)高于配置文件。
源碼位置:
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
new CommandLinePropertySource<>("commandLineArgs", args);
}命令行參數(shù)會(huì)被解析成 key-value 形式,覆蓋之前加載的同名配置。
3.5 @Value 與 @ConfigurationProperties 獲取配置
@Value
直接從 Environment 中取值:
@Value("${server.port}")
private int port;
@ConfigurationProperties
批量綁定配置到 Java Bean:
@Component
@ConfigurationProperties(prefix = "myapp")
public class MyAppConfig {
private String name;
private int timeout;
// getters and setters
}application.properties:
myapp.name=DemoApp myapp.timeout=30
這樣 MyAppConfig 會(huì)自動(dòng)綁定配置值。
?? 小結(jié):
Environment是配置的“中央倉庫”。- 配置源加載是分階段進(jìn)行的,且有明確的優(yōu)先級(jí)。
- 命令行參數(shù)幾乎總是能覆蓋所有配置文件值。
@Value適合單個(gè)配置值,@ConfigurationProperties適合批量綁定。
4. ApplicationContext的創(chuàng)建與刷新
4.1 ApplicationContext 的類型選擇
在 SpringApplication.createApplicationContext() 中,會(huì)根據(jù) WebApplicationType 選擇不同的 ApplicationContext 實(shí)現(xiàn):
| 應(yīng)用類型 | ApplicationContext 實(shí)現(xiàn)類 |
|---|---|
| SERVLET | AnnotationConfigServletWebServerApplicationContext |
| REACTIVE | AnnotationConfigReactiveWebServerApplicationContext |
| NONE | AnnotationConfigApplicationContext |
源碼(簡化):
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(
"org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
break;
case REACTIVE:
contextClass = Class.forName(
"org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
break;
default:
contextClass = Class.forName(
"org.springframework.context.annotation.AnnotationConfigApplicationContext");
}
} catch (ClassNotFoundException ex) {
throw new IllegalStateException("Unable to create ApplicationContext", ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
重點(diǎn):
- SERVLET 類型不僅是 IoC 容器,還會(huì)管理內(nèi)嵌的 Servlet Web 服務(wù)器(Tomcat/Jetty/Undertow)。
- REACTIVE 類型則會(huì)啟動(dòng) Netty 作為反應(yīng)式 Web 服務(wù)器。
- NONE 類型適用于純后端任務(wù)(無Web功能)。
4.2 refreshContext() 核心邏輯解析
refreshContext() 會(huì)調(diào)用 Spring Framework 的 AbstractApplicationContext.refresh(),這是 IoC 容器生命周期的核心方法。
簡化執(zhí)行步驟(以 Servlet 應(yīng)用為例):
- 準(zhǔn)備刷新上下文
- 初始化屬性源(PropertySources)
- 驗(yàn)證必需的屬性是否存在
- 獲取并準(zhǔn)備 BeanFactory
- 創(chuàng)建
DefaultListableBeanFactory
- 創(chuàng)建
- 執(zhí)行 BeanFactoryPostProcessor
- 在 Bean 定義加載后、實(shí)例化前執(zhí)行
- 注冊(cè) BeanPostProcessor
- 在 Bean 實(shí)例化前后進(jìn)行增強(qiáng)(如 @Autowired 注入、AOP 代理)
- 初始化消息源(MessageSource)
- 用于國際化
- 初始化事件廣播器(ApplicationEventMulticaster)
- 創(chuàng)建內(nèi)嵌 Web 服務(wù)器(ServletWebServerFactory)并啟動(dòng)
- 實(shí)例化剩余的單例 Bean
- 完成刷新
- 發(fā)布
ContextRefreshedEvent
- 發(fā)布
4.3 BeanFactoryPostProcessor 與 BeanPostProcessor 執(zhí)行順序
很多擴(kuò)展功能(比如 MyBatis、Spring Cloud)都是依賴這兩個(gè)接口來修改 Bean 定義或 Bean 實(shí)例的。
執(zhí)行順序:
- BeanFactoryPostProcessor
- 作用:修改 Bean 定義元數(shù)據(jù)(
BeanDefinition) - 運(yùn)行時(shí)機(jī):Bean 實(shí)例化 之前
- 常見實(shí)現(xiàn):
ConfigurationClassPostProcessor(處理 @Configuration、@Bean)
- 作用:修改 Bean 定義元數(shù)據(jù)(
- BeanPostProcessor
- 作用:增強(qiáng) Bean 實(shí)例(代理、依賴注入等)
- 運(yùn)行時(shí)機(jī):Bean 實(shí)例化 前后
- 常見實(shí)現(xiàn):
AutowiredAnnotationBeanPostProcessor(處理 @Autowired)、AopProxyCreator
示例:自定義 BeanPostProcessor
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (beanName.equals("myService")) {
System.out.println("Before init: " + beanName);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (beanName.equals("myService")) {
System.out.println("After init: " + beanName);
}
return bean;
}
}4.4 BootstrapContext 的作用
在 Spring Boot 2.4 之后引入 BootstrapContext,它是應(yīng)用啟動(dòng)早期的一個(gè)輕量級(jí)上下文,用于注冊(cè)啟動(dòng)前所需的組件,典型用途是:
- 注冊(cè)配置加載器(Spring Cloud Config 使用)
- 注冊(cè)早期的監(jiān)控或指標(biāo)收集組件
源碼片段:
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ApplicationEnvironmentPreparedEvent event =
new ApplicationEnvironmentPreparedEvent(this, args, environment);
listeners.environmentPrepared(bootstrapContext, environment);
?? 小結(jié):
- ApplicationContext 是整個(gè)應(yīng)用的核心容器,負(fù)責(zé) Bean 生命周期、事件、資源管理。
- refresh() 是啟動(dòng)過程中 Bean 加載的核心方法。
- BeanFactoryPostProcessor 改定義,BeanPostProcessor 改實(shí)例。
- BootstrapContext 在早期階段提供了擴(kuò)展能力。
5. 自動(dòng)配置與條件化裝配
Spring Boot最大的亮點(diǎn)之一,就是它的自動(dòng)配置(Auto-Configuration)機(jī)制——你只需要引入依賴,不用手動(dòng)配置,相關(guān)的Bean就會(huì)自動(dòng)幫你注冊(cè)好。
這一章我們會(huì)拆開它的底層原理,逐步分析:
5.1 @EnableAutoConfiguration 原理
在Spring Boot應(yīng)用的啟動(dòng)類中,我們經(jīng)常會(huì)看到:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@SpringBootApplication 是一個(gè)組合注解,內(nèi)部包含了:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)}
)
public @interface SpringBootApplication {
// ...
}
核心就是 @EnableAutoConfiguration。
源碼入口
@EnableAutoConfiguration 本身也只是一個(gè)注解,它通過 @Import 引入了 AutoConfigurationImportSelector:
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
重點(diǎn)來了:
@Import會(huì)把AutoConfigurationImportSelector這個(gè)類加入到容器啟動(dòng)過程中。- 這個(gè)類會(huì)掃描所有
META-INF/spring.factories中聲明的自動(dòng)配置類。
5.2 SpringFactoriesLoader 與 META-INF/spring.factories
SpringFactoriesLoader 是一個(gè)幫助類,負(fù)責(zé)加載 META-INF/spring.factories 文件中定義的類名列表。
典型的 spring.factories 文件:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
工作流程:
SpringFactoriesLoader.loadFactoryNames()會(huì)掃描所有依賴包下的META-INF/spring.factories文件。- 讀取 key 為
org.springframework.boot.autoconfigure.EnableAutoConfiguration的配置值。 - 把這些類(通常以
xxxAutoConfiguration結(jié)尾)交給Spring容器去加載。
源碼關(guān)鍵點(diǎn)(SpringFactoriesLoader)
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
- 這里的
factoryType就是EnableAutoConfiguration.class - 最終得到一個(gè)自動(dòng)配置類的全限定名列表
5.3 條件化注解(@ConditionalXXX)
自動(dòng)配置類并不是無條件生效的,它們會(huì)結(jié)合一系列條件注解判斷當(dāng)前環(huán)境是否滿足,再?zèng)Q定是否注冊(cè)Bean。
常見的條件注解:
| 注解 | 作用 |
|---|---|
@ConditionalOnClass | 當(dāng)類路徑存在指定類時(shí)生效 |
@ConditionalOnMissingClass | 當(dāng)類路徑不存在指定類時(shí)生效 |
@ConditionalOnBean | 當(dāng)容器中存在指定Bean時(shí)生效 |
@ConditionalOnMissingBean | 當(dāng)容器中不存在指定Bean時(shí)生效 |
@ConditionalOnProperty | 當(dāng)配置文件中存在某個(gè)屬性并且值匹配時(shí)生效 |
@ConditionalOnWebApplication | 當(dāng)應(yīng)用是Web類型時(shí)生效 |
@ConditionalOnNotWebApplication | 當(dāng)應(yīng)用不是Web類型時(shí)生效 |
@ConditionalOnExpression | 根據(jù)SpEL表達(dá)式結(jié)果決定是否生效 |
例子(JacksonAutoConfiguration):
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ObjectMapper.class)
@EnableConfigurationProperties(JacksonProperties.class)
public class JacksonAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
return builder.createXmlMapper(false).build();
}
}解釋:
- 如果Classpath中有
ObjectMapper類(Jackson庫),則配置生效。 - 如果容器中沒有
ObjectMapper,則自動(dòng)注冊(cè)一個(gè)。
5.4 自定義自動(dòng)配置類實(shí)戰(zhàn)
假設(shè)我們想在Spring Boot啟動(dòng)時(shí),自動(dòng)配置一個(gè) HelloService:
1. 編寫服務(wù)類
public class HelloService {
public String sayHello(String name) {
return "Hello, " + name;
}
}
2. 編寫自動(dòng)配置類
@Configuration
@ConditionalOnClass(HelloService.class)
public class HelloServiceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public HelloService helloService() {
return new HelloService();
}
}3. 配置 META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.demo.config.HelloServiceAutoConfiguration
4. 引入依賴后直接使用
@RestController
public class HelloController {
@Autowired
private HelloService helloService;
@GetMapping("/hello")
public String hello(String name) {
return helloService.sayHello(name);
}
}無需額外配置,訪問 /hello?name=Boot 即可看到自動(dòng)配置效果。
5.5 條件注解(@ConditionalXXX)的源碼執(zhí)行流程
Spring Boot 的自動(dòng)配置類之所以能“按需生效”,核心就是依賴 Spring Framework 提供的 條件化裝配機(jī)制,也就是 @Conditional。
5.5.1 條件注解的繼承關(guān)系
所有 @ConditionalOnXxx 注解,最終都是 @Conditional 的語法糖。
舉個(gè)例子:
@ConditionalOnClass(ObjectMapper.class)
展開看源碼:
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
Class<?>[] value() default {};
String[] name() default {};
}
可以看到:
- 它其實(shí)就是
@Conditional,只不過指定了一個(gè)Condition實(shí)現(xiàn)類:OnClassCondition。 - 也就是說,
@ConditionalOnClass-> OnClassCondition -> 判斷classpath是否存在指定類。
5.5.2 條件評(píng)估流程
條件注解是在 Spring 容器啟動(dòng)階段,由 ConfigurationClassPostProcessor 解析 @Configuration 類時(shí)進(jìn)行評(píng)估的。
流程大致是:
- 掃描到自動(dòng)配置類
ConfigurationClassParser解析自動(dòng)配置類上的注解。
- 遇到 @Conditional
- 調(diào)用
ConditionEvaluator.shouldSkip()方法。
- 調(diào)用
- 找到對(duì)應(yīng) Condition 實(shí)現(xiàn)類
- 比如
OnClassCondition,會(huì)去執(zhí)行matches()方法。
- 比如
- 返回 true/false
true表示條件滿足 -> 該配置類或方法會(huì)注冊(cè)到容器false表示條件不滿足 -> 直接跳過
源碼片段(ConditionEvaluator)
public boolean shouldSkip(AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
if (metadata.isAnnotated(Conditional.class.getName())) {
List<Condition> conditions = getConditionClasses(metadata);
for (Condition condition : conditions) {
if (!condition.matches(context, metadata)) {
return true; // 跳過
}
}
}
return false;
}
重點(diǎn):
matches()方法是判斷條件是否成立的地方。- 每個(gè)
@ConditionalOnXxx注解都有自己的 Condition 實(shí)現(xiàn)類。
5.5.3 常見 Condition 實(shí)現(xiàn)類
| 條件注解 | 對(duì)應(yīng) Condition 類 | 作用 |
|---|---|---|
@ConditionalOnClass | OnClassCondition | 檢查類路徑是否存在指定類 |
@ConditionalOnMissingClass | OnClassCondition | 檢查類路徑是否不存在指定類 |
@ConditionalOnBean | OnBeanCondition | 檢查容器中是否存在指定 Bean |
@ConditionalOnMissingBean | OnBeanCondition | 檢查容器中是否不存在指定 Bean |
@ConditionalOnProperty | OnPropertyCondition | 檢查配置文件中屬性是否滿足 |
@ConditionalOnWebApplication | OnWebApplicationCondition | 檢查應(yīng)用是否是 Web 環(huán)境 |
@ConditionalOnExpression | OnExpressionCondition | 執(zhí)行 SpEL 表達(dá)式并判斷結(jié)果 |
5.5.4 執(zhí)行順序與調(diào)試技巧
- 條件判斷會(huì)在 BeanDefinition 注冊(cè)階段執(zhí)行,而不是Bean實(shí)例化階段。
- 你可以在
Condition.matches()方法里打斷點(diǎn),啟動(dòng)項(xiàng)目,就能看到哪些條件返回 false,從而排查自動(dòng)配置沒生效的原因。 - Spring Boot 提供了一個(gè)
--debug啟動(dòng)參數(shù),可以打印出條件評(píng)估報(bào)告。
命令行啟動(dòng)
java -jar demo.jar --debug
部分輸出
=========================
CONDITIONS EVALUATION REPORT
=========================
Positive matches:
-----------------
JacksonAutoConfiguration matched:
- @ConditionalOnClass found required class 'com.fasterxml.jackson.databind.ObjectMapper' (OnClassCondition)
Negative matches:
-----------------
DataSourceAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'javax.sql.DataSource' (OnClassCondition)這樣你就能非常清楚地知道某個(gè)自動(dòng)配置類為什么沒生效。
5.6 自動(dòng)配置的優(yōu)先級(jí)與覆蓋規(guī)則
Spring Boot 設(shè)計(jì)的自動(dòng)配置是低優(yōu)先級(jí)的,這樣開發(fā)者可以隨時(shí)覆蓋默認(rèn)配置。
5.6.1 自動(dòng)配置的核心原則
- 用戶優(yōu)先(開發(fā)者自己配置的 Bean 優(yōu)先于 Spring Boot 提供的 Bean)
- 條件觸發(fā)(只有在條件成立時(shí),自動(dòng)配置才會(huì)生效)
- 可覆蓋可禁用(你可以通過配置文件或注解控制自動(dòng)配置是否啟用)
5.6.2 自動(dòng)配置的低優(yōu)先級(jí)實(shí)現(xiàn)方式
1.@ConditionalOnMissingBean
- 大多數(shù)自動(dòng)配置類在注冊(cè) Bean 時(shí)都會(huì)加上這個(gè)條件。
- 表示:如果容器中已經(jīng)有這個(gè) Bean,就不再創(chuàng)建默認(rèn)的 Bean。
示例:
@Bean
@ConditionalOnMissingBean
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
意思是:
- 如果你自己在配置類里聲明了
ObjectMapperBean,那么 Boot 的這個(gè)方法不會(huì)執(zhí)行。
2.@AutoConfigureBefore/@AutoConfigureAfter
- 用來指定自動(dòng)配置類之間的順序。
- 比如:
@AutoConfigureBefore(JacksonAutoConfiguration.class)
public class MyCustomJsonConfig { ... }
這樣你的配置會(huì)在 JacksonAutoConfiguration 之前執(zhí)行。
3.@Primary
- 如果你想讓多個(gè)同類型 Bean 中某一個(gè)優(yōu)先被注入,可以加上
@Primary。 - 這不是自動(dòng)配置特有的,但常用于覆蓋默認(rèn) Bean。
4.spring.main.allow-bean-definition-overriding
- Spring Boot 2.1 以后默認(rèn)不允許 Bean 覆蓋,如果你聲明了和默認(rèn)配置同名的 Bean 會(huì)報(bào)錯(cuò)。
- 解決辦法:
spring:
main:
allow-bean-definition-overriding: true這樣你的 Bean 會(huì)覆蓋默認(rèn) Bean。
5.6.3 禁用某個(gè)自動(dòng)配置
有三種常用方式:
方式 1:@SpringBootApplication(exclude = ...)
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class MyApp {}
方式 2:配置文件
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration方式 3:META-INF/spring.factories
- 這種是你自己寫 starter 時(shí)用的,普通項(xiàng)目一般不直接操作。
5.6.4 調(diào)試 Bean 覆蓋問題
- 啟動(dòng)加
--debug看條件評(píng)估報(bào)告。 - 使用
ApplicationContext打印所有 Bean:
Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
如果報(bào) BeanDefinitionOverrideException,先確認(rèn)是否是自己命名沖突,必要時(shí)允許覆蓋。
6. Spring Boot啟動(dòng)流程的擴(kuò)展點(diǎn)
Spring Boot的設(shè)計(jì)理念之一是高度的可擴(kuò)展性。在啟動(dòng)過程中,Spring Boot提供了豐富的擴(kuò)展點(diǎn),允許開發(fā)者在不同階段插入自定義邏輯,滿足多樣化的業(yè)務(wù)需求。這一章將詳細(xì)介紹Spring Boot啟動(dòng)流程中常用的擴(kuò)展點(diǎn),包括SpringApplicationRunListener、ApplicationListener以及SpringApplicationBuilder的鏈?zhǔn)絾?dòng)配置。
6.1 SpringApplicationRunListener —— 啟動(dòng)事件監(jiān)聽器
6.1.1 作用與觸發(fā)時(shí)機(jī)
SpringApplicationRunListener接口用于監(jiān)聽Spring Boot啟動(dòng)流程的各個(gè)關(guān)鍵階段。它的核心目的是在啟動(dòng)流程的不同時(shí)間點(diǎn)觸發(fā)回調(diào),幫助開發(fā)者插入自定義操作或日志打印等輔助功能。
Spring Boot啟動(dòng)時(shí)會(huì)依次調(diào)用SpringApplicationRunListener中的如下事件方法:
starting():啟動(dòng)初始化,Spring Boot啟動(dòng)的第一個(gè)階段。environmentPrepared(ConfigurableEnvironment environment):環(huán)境準(zhǔn)備完成,配置已加載。contextPrepared(ConfigurableApplicationContext context):ApplicationContext創(chuàng)建完成但尚未刷新。contextLoaded(ConfigurableApplicationContext context):ApplicationContext加載完成。started(ConfigurableApplicationContext context):ApplicationContext刷新完成。running(ConfigurableApplicationContext context):應(yīng)用準(zhǔn)備就緒,進(jìn)入運(yùn)行狀態(tài)。failed(ConfigurableApplicationContext context, Throwable exception):啟動(dòng)失敗回調(diào)。
6.1.2 Spring Boot內(nèi)部實(shí)現(xiàn)及自動(dòng)加載
Spring Boot通過META-INF/spring.factories文件自動(dòng)加載所有注冊(cè)的SpringApplicationRunListener實(shí)現(xiàn)。常見的內(nèi)置實(shí)現(xiàn)包括:
EventPublishingRunListener:負(fù)責(zé)將啟動(dòng)事件發(fā)布到Spring事件機(jī)制。ClasspathLoggingApplicationListener:啟動(dòng)時(shí)打印類路徑信息。StartupInfoLogger:打印啟動(dòng)信息統(tǒng)計(jì)。
自動(dòng)加載示例(spring.factories文件):
org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener,\ org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\ org.springframework.boot.context.logging.LoggingApplicationListener
6.1.3 自定義 SpringApplicationRunListener
開發(fā)者可以通過實(shí)現(xiàn)SpringApplicationRunListener接口來自定義啟動(dòng)事件監(jiān)聽器。實(shí)現(xiàn)步驟:
- 實(shí)現(xiàn)接口并提供帶
SpringApplication和String[] args構(gòu)造函數(shù)。 - 在
META-INF/spring.factories注冊(cè)監(jiān)聽器類。
示例代碼:
public class MyRunListener implements SpringApplicationRunListener {
public MyRunListener(SpringApplication app, String[] args) {
// 構(gòu)造器,必須包含這兩個(gè)參數(shù)
}
@Override
public void starting() {
System.out.println("應(yīng)用開始啟動(dòng)");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
System.out.println("環(huán)境已準(zhǔn)備好");
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("上下文已準(zhǔn)備好");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("上下文已加載");
}
@Override
public void started(ConfigurableApplicationContext context) {
System.out.println("應(yīng)用已啟動(dòng)");
}
@Override
public void running(ConfigurableApplicationContext context) {
System.out.println("應(yīng)用正在運(yùn)行");
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("應(yīng)用啟動(dòng)失敗,異常信息:" + exception.getMessage());
}
}注冊(cè)監(jiān)聽器:
org.springframework.boot.SpringApplicationRunListener=com.example.MyRunListener
6.2 ApplicationListener —— Spring事件監(jiān)聽器
6.2.1 作用與應(yīng)用場(chǎng)景
ApplicationListener是Spring框架的核心事件監(jiān)聽接口,支持監(jiān)聽包括啟動(dòng)事件在內(nèi)的所有Spring事件。它基于Spring的事件發(fā)布機(jī)制,允許開發(fā)者對(duì)特定事件做出響應(yīng)。
相比SpringApplicationRunListener,ApplicationListener更通用,不僅限于啟動(dòng)階段事件,還可監(jiān)聽?wèi)?yīng)用運(yùn)行過程中的各種事件。
6.2.2 常見啟動(dòng)事件類型
| 事件類 | 觸發(fā)時(shí)機(jī) |
|---|---|
ApplicationStartingEvent | Spring Boot啟動(dòng)最開始時(shí) |
ApplicationEnvironmentPreparedEvent | 環(huán)境準(zhǔn)備完成時(shí) |
ApplicationPreparedEvent | ApplicationContext創(chuàng)建完畢,準(zhǔn)備刷新 |
ApplicationStartedEvent | ApplicationContext刷新成功 |
ApplicationReadyEvent | 應(yīng)用啟動(dòng)完成,準(zhǔn)備接收請(qǐng)求 |
ApplicationFailedEvent | 應(yīng)用啟動(dòng)失敗 |
6.2.3 自定義ApplicationListener示例
@Component
public class MyReadyListener implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
System.out.println("應(yīng)用已準(zhǔn)備就緒,執(zhí)行自定義初始化操作...");
// 例如啟動(dòng)緩存預(yù)熱、消息監(jiān)聽器等
}
}6.3 SpringApplicationBuilder —— 鏈?zhǔn)絾?dòng)配置工具
6.3.1 設(shè)計(jì)理念與優(yōu)勢(shì)
SpringApplicationBuilder是構(gòu)建SpringApplication實(shí)例的鏈?zhǔn)綐?gòu)建器,支持更靈活、優(yōu)雅的啟動(dòng)配置,適合復(fù)雜啟動(dòng)場(chǎng)景。
使用SpringApplicationBuilder,可以逐步疊加各種配置,如初始化器、監(jiān)聽器、激活Profile等,并最后調(diào)用run()方法啟動(dòng)應(yīng)用。
6.3.2 常用API與示例
sources(Class<?>... sources):設(shè)置主配置類或額外配置類。initializers(ApplicationContextInitializer<?>... initializers):添加上下文初始化器。listeners(ApplicationListener<?>... listeners):添加事件監(jiān)聽器。profiles(String... profiles):激活指定環(huán)境。run(String... args):啟動(dòng)應(yīng)用。
示例:
new SpringApplicationBuilder()
.sources(MyApplication.class)
.initializers(new MyContextInitializer())
.listeners(new MyReadyListener())
.profiles("dev")
.run(args);
6.3.3 自定義ApplicationContextInitializer示例
public class MyContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("ApplicationContext初始化邏輯...");
// 可以動(dòng)態(tài)注冊(cè)Bean、修改環(huán)境變量等
}
}6.4 小結(jié)
- Spring Boot啟動(dòng)流程通過
SpringApplicationRunListener提供了豐富的啟動(dòng)階段監(jiān)聽能力,適合執(zhí)行啟動(dòng)監(jiān)控、日志收集、資源準(zhǔn)備等任務(wù)。 ApplicationListener基于Spring事件機(jī)制,支持對(duì)啟動(dòng)事件及其他業(yè)務(wù)事件的監(jiān)聽,更加通用。SpringApplicationBuilder是鏈?zhǔn)綐?gòu)建啟動(dòng)參數(shù)的工具,極大提升了啟動(dòng)配置的靈活性和可維護(hù)性。
通過掌握這幾個(gè)核心擴(kuò)展點(diǎn),開發(fā)者可以對(duì)Spring Boot啟動(dòng)流程進(jìn)行高度自定義,滿足各種復(fù)雜的啟動(dòng)需求,提升應(yīng)用的健壯性和擴(kuò)展能力。
7. Spring Boot啟動(dòng)性能優(yōu)化
隨著企業(yè)級(jí)應(yīng)用復(fù)雜度的提升,Spring Boot應(yīng)用的啟動(dòng)時(shí)間成為一個(gè)重要考量指標(biāo)。快速啟動(dòng)不僅提升開發(fā)效率,還能改善云環(huán)境中的彈性擴(kuò)縮容體驗(yàn)。此章將圍繞啟動(dòng)性能展開,講解啟動(dòng)耗時(shí)分析方法、常見瓶頸,以及針對(duì)性的優(yōu)化策略。
7.1 啟動(dòng)耗時(shí)分析:利用StopWatch
7.1.1 StopWatch在Spring Boot中的應(yīng)用
在SpringApplication.run()方法中,Spring Boot通過org.springframework.util.StopWatch來度量啟動(dòng)各個(gè)階段的耗時(shí),幫助開發(fā)者定位瓶頸。
簡化代碼片段(摘自SpringApplication.run()):
StopWatch stopWatch = new StopWatch();
stopWatch.start("SpringApplication啟動(dòng)");
// 啟動(dòng)流程中的重要階段代碼...
stopWatch.stop();
System.out.println(stopWatch.prettyPrint());StopWatch會(huì)輸出類似如下的階段耗時(shí)報(bào)告:
StopWatch '': running time = 12345 ns
---------------------------------------------
ns % Task name
---------------------------------------------
5000000 40% SpringApplication啟動(dòng)
7500000 60% ApplicationContext刷新
7.1.2 使用啟動(dòng)日志和--debug參數(shù)定位耗時(shí)
Spring Boot支持通過命令行參數(shù)--debug開啟詳細(xì)日志,打印啟動(dòng)過程中的細(xì)節(jié)信息,便于發(fā)現(xiàn)耗時(shí)較長的組件或階段。
java -jar myapp.jar --debug
日志中會(huì)包含大量有用信息,如自動(dòng)配置條件匹配、Bean創(chuàng)建時(shí)間等。
7.2 主要啟動(dòng)性能瓶頸分析
- 自動(dòng)配置掃描與條件判斷:復(fù)雜的
@Conditional注解邏輯,條件不匹配時(shí)仍需執(zhí)行判斷,影響啟動(dòng)速度。 - Bean實(shí)例化和依賴注入:大量單例Bean的創(chuàng)建、復(fù)雜依賴關(guān)系的解析耗時(shí)較長。
- 環(huán)境變量和配置加載:讀取多層配置文件和系統(tǒng)屬性,尤其是網(wǎng)絡(luò)配置(如遠(yuǎn)程配置中心)可能阻塞啟動(dòng)。
- 類路徑掃描與組件掃描:掃描大量包及類時(shí)會(huì)增加啟動(dòng)負(fù)擔(dān)。
- 嵌入式容器啟動(dòng):內(nèi)嵌Tomcat、Jetty或Undertow啟動(dòng)耗時(shí)不可忽視。
7.3 啟動(dòng)性能優(yōu)化策略
7.3.1 使用@Lazy延遲加載Bean
通過給不必要立即加載的Bean添加@Lazy注解,Spring容器會(huì)延遲初始化,減少啟動(dòng)時(shí)的Bean加載壓力。
示例:
@Configuration
public class MyConfig {
@Bean
@Lazy
public HeavyService heavyService() {
return new HeavyService();
}
}7.3.2 精準(zhǔn)配置@ComponentScan掃描范圍
縮小@ComponentScan的掃描范圍,避免掃描無關(guān)包,減少類路徑掃描時(shí)間。
@SpringBootApplication(scanBasePackages = "com.example.myapp.service")
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
7.3.3 禁用不必要的自動(dòng)配置
通過@SpringBootApplication(exclude = {AutoConfigurationClass.class})排除不需要的自動(dòng)配置類,避免無用配置初始化。
示例:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class MyApplication { ... }
7.3.4 自定義自動(dòng)配置條件減少啟動(dòng)判斷
簡化自動(dòng)配置中的@Conditional注解判斷邏輯,避免復(fù)雜條件鏈。
7.3.5 采用Spring Boot Layered JAR優(yōu)化啟動(dòng)
Spring Boot 2.3+支持分層Jar結(jié)構(gòu),將應(yīng)用、依賴、啟動(dòng)器分開打包,有助于加速Docker鏡像啟動(dòng)。
7.4 實(shí)踐示例:啟動(dòng)時(shí)間統(tǒng)計(jì)與延遲加載
@SpringBootApplication
public class PerformanceApp {
public static void main(String[] args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start("啟動(dòng)應(yīng)用");
SpringApplication app = new SpringApplication(PerformanceApp.class);
app.run(args);
stopWatch.stop();
System.out.println(stopWatch.prettyPrint());
}
@Bean
@Lazy
public ExpensiveBean expensiveBean() {
System.out.println("初始化ExpensiveBean...");
return new ExpensiveBean();
}
}運(yùn)行該程序,可以看到啟動(dòng)階段時(shí)間統(tǒng)計(jì),并且expensiveBean只有在首次調(diào)用時(shí)才初始化。
7.5 總結(jié)
- 通過
StopWatch和--debug日志,能有效定位啟動(dòng)耗時(shí)階段。 - 延遲加載、精準(zhǔn)掃描、排除無用自動(dòng)配置是主要優(yōu)化手段。
- 優(yōu)化啟動(dòng)性能有助于提升開發(fā)效率、加快部署速度、降低云環(huán)境資源消耗。
到此這篇關(guān)于Spring Boot啟動(dòng)流程詳解的文章就介紹到這了,更多相關(guān)Spring Boot啟動(dòng)流程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot整合Spring?Boot?Admin實(shí)現(xiàn)服務(wù)監(jiān)控的方法
這篇文章主要介紹了SpringBoot整合Spring?Boot?Admin實(shí)現(xiàn)服務(wù)監(jiān)控,內(nèi)容包括Server端服務(wù)開發(fā),Client端服務(wù)開發(fā)其中Spring Boot Admin還可以對(duì)其監(jiān)控的服務(wù)提供告警功能,如服務(wù)宕機(jī)時(shí),可以及時(shí)以郵件方式通知運(yùn)維人員,感興趣的朋友跟隨小編一起看看吧2022-03-03
SpringCloud Zuul和Gateway的實(shí)例代碼(搭建方式)
本文主要介紹了SpringCloudZuul和SpringCloudGateway的簡單示例,SpringCloudGateway是推薦使用的API網(wǎng)關(guān)解決方案,基于SpringFramework5和ProjectReactor構(gòu)建,具有更高的性能和吞吐量2025-02-02
Java+opencv3.2.0實(shí)現(xiàn)模板匹配
這篇文章主要為大家詳細(xì)介紹了Java+opencv3.2.0實(shí)現(xiàn)模板匹配的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02
Spring BeanPostProcessor源碼示例解析
這篇文章主要為大家介紹了Spring BeanPostProcessor源碼示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
java獲取新insert數(shù)據(jù)自增id的實(shí)現(xiàn)方法
這篇文章主要介紹了java獲取新insert數(shù)據(jù)自增id的實(shí)現(xiàn)方法,在具體生成id的時(shí)候,我們的操作順序一般是:先在主表中插入記錄,然后獲得自動(dòng)生成的id,以它為基礎(chǔ)插入從表的記錄,需要的朋友可以參考下2019-06-06
Maven在Java8下如何忽略Javadoc的編譯錯(cuò)誤詳解
這篇文章主要給大家介紹了關(guān)于Maven在Java8下如何忽略Javadoc的編譯錯(cuò)誤的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-08-08
idea使用war以及war exploded的區(qū)別說明
本文詳細(xì)解析了war與warexploded兩種部署方式的差異及步驟,war方式是先打包成war包,再部署到服務(wù)器上;warexploded方式是直接把文件夾、class文件等移到Tomcat上部署,支持熱部署,開發(fā)時(shí)常用,文章分別列出了warexploded模式和war包形式的具體操作步驟2024-10-10

