承接上篇 SpringApplication 初始化,本文深度剖析 run() 方法的 10 大核心階段,從環境準備到上下文刷新,完整呈現 Spring Boot 啟動生命週期。
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
// 1. 獲取事件發布器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(); // 發布 ApplicationStartingEvent
try {
// 2. 核心執行流程
context = doRun(listeners, args);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
} catch (Throwable ex) {
handleRunFailure(context, listeners, ex);
throw new IllegalStateException(ex);
}
}
ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, args); listeners.environmentPrepared(environment); // 發布 ApplicationEnvironmentPreparedEvent
Environment 組成:
if (isWebEnvironment(environment) && !this.webEnvironment) {
environment = convertToStandardEnvironment(environment);
}
if (this.bannerMode != Banner.Mode.OFF) {
printBanner(environment);
}
context = createApplicationContext();
類型決定邏輯:
AnnotationConfigServletWebServerApplicationContextAnnotationConfigReactiveWebServerApplicationContextAnnotationConfigApplicationContextcontext.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); // 執行所有 ApplicationContextInitializer.initialize() listeners.contextPrepared(context); // 發布 ApplicationContextPreparedEvent
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments);
Set
load() 核心邏輯:
protected void load(ApplicationContext context, Object[] sources) {
BeanDefinitionLoader loader = new BeanDefinitionLoader(context);
for (Object source : sources) {
if (source instanceof Class) {
loader.load((Class>) source);
} else if (source instanceof Resource) {
loader.load((Resource) source);
} else if (source instanceof Package) {
loader.load((Package) source);
} else if (source instanceof CharSequence) {
loader.load((CharSequence) source);
} else {
throw new IllegalArgumentException("Invalid source type");
}
}
}
關鍵點:@SpringBootApplication → @EnableAutoConfiguration → AutoConfigurationImportSelector → 掃描 META-INF/spring.factories 中 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的所有自動配置類 → 根據條件註解(@ConditionalOnClass、@ConditionalOnMissingBean 等)決定載入哪些 Bean 定義。
listeners.contextLoaded(context); // 發布 ApplicationPreparedEvent // 此時所有 ApplicationListener 已註冊到 ApplicationContext
refresh(context); // AbstractApplicationContext.refresh() // 刷新核心子流程: // 1. prepareRefresh() - 驗證、鎖定、啟動啟動 // 2. obtainFreshBeanFactory() - 建立/獲取 BeanFactory // 3. prepareBeanFactory() - 配置 BeanFactory(後置處理器、忽略介面等) // 4. postProcessBeanFactory() - 子類擴充點 // 5. invokeBeanFactoryPostProcessors() - 執行 BeanFactoryPostProcessor(ConfigurationClassPostProcessor 處理 @Configuration/@ComponentScan 等) // 6. registerBeanPostProcessors() - 註冊 BeanPostProcessor // 7. initMessageSource() - 國際化 // 8. initApplicationEventMulticaster() - 事件多播器 // 9. onRefresh() - 子類擴充(ServletWebServerApplicationContext 建立內嵌 Tomcat/Jetty/Undertow) // 10. registerListeners() - 註冊 ApplicationListener // 11. finishBeanFactoryInitialization() - 實例化所有非懶加載單例 Bean // 12. finishRefresh() - 清理、發布 ContextRefreshedEvent、註冊 MBean
if (this.registerShutdownHook) {
try {
context.registerShutdownHook(); // JVM 關閉時優雅關閉上下文
} catch (AccessControlException ex) {
// 某些環境(如測試)不允許
}
}
afterRefresh(context, applicationArguments); listeners.finished(context, null); // 發布 ApplicationReadyEvent / ApplicationFailedEvent
SpringApplication.run()
│
├─▶ listeners.starting()
│ └─▶ ApplicationStartingEvent
│
├─▶ environmentPrepared()
│ └─▶ ApplicationEnvironmentPreparedEvent
│ ├─▶ ConfigFileApplicationListener → 載入 application.yml
│ └─▶ LoggingApplicationListener → 初始化日誌系統
│
├─▶ contextPrepared()
│ └─▶ ApplicationContextPreparedEvent
│
├─▶ contextLoaded()
│ └─▶ ApplicationPreparedEvent
│ ├─▶ 註冊所有 ApplicationListener 到 Context
│ └─▶ 啟動內嵌 Servlet 容器(onRefresh 階段)
│
├─▶ refresh()
│ └─▶ ContextRefreshedEvent (finishRefresh 階段)
│
└─▶ listeners.finished()
└─▶ ApplicationReadyEvent (成功) / ApplicationFailedEvent (失敗)
├─▶ 應用完全就緒,可處理請求
└─▶ 適合執行:健康檢查、緩存預熱、排程啟動
| 擴充點 | 介面/註解 | 觸發時機 | 典型用途 |
|---|---|---|---|
| ApplicationContextInitializer | initialize(ConfigurableApplicationContext) | contextPrepared 前 | 動態註冊 PropertySource、配置初始 Bean |
| ApplicationListener | onApplicationEvent(Event) | 各階段事件發布時 | 啟動監控、配置載入、日誌初始化 |
| BeanFactoryPostProcessor | postProcessBeanFactory(BeanFactory) | refresh() 階段 5 | 掃描配置類、處理 @Value、動態註冊 BeanDefinition |
| BeanPostProcessor | postProcessBefore/AfterInitialization | Bean 初始化前後 | AOP 代理、依賴注入、驗證、修改 Bean |
| SmartInitializingSingleton | afterSingletonsInstantiated() | 所有單例 Bean 建立後 | 啟動後初始化任務、註冊監控指標 |
| ApplicationRunner/CommandLineRunner | run(args) | finished() 事件後 | 啟動時業務邏輯、資料遷移、緩存預熱 |
@ConditionalOnClass、@ConditionalOnMissingBean 減少不必要的 Bean 定義載入@Lazy、spring.main.lazy-initialization=true 推遲非核心 Bean 建立@ComponentScan basePackages,避免全盤掃描@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})-XX:TieredStopAtLevel=1(開發模式)、-Xshare:on(CDS 共享類別資料)