本文是 Spring Boot 4 系列第 10 篇 | 基于 Spring Boot 4.1.0 GA 源码 + Spring Framework 7.0.x + JDK 17 基线 | 预计阅读 30 分钟
文末附「启动流程 13 阶段总表」和「三大扩展点」,排查启动问题时可直接对照。
写在前面
main() 方法第一行代码执行时,到 ApplicationRunner 开始执行,JVM 里到底发生了什么?这个问题搞清楚了,启动慢的排查、自定义启动逻辑、给 Spring Boot 贡献代码,都有了着手点。
这篇用"跟着断点走"的方式,完整拆解 SpringApplication.run() 从调用到应用就绪的全部 13 个阶段(阶段 0 ~ 阶段 12),所有包路径和类名均来自 Spring Boot 4.1.0 实际源码。
内容速览
- 启动流程全景图:从
main()到ApplicationReadyEvent的完整链路 - 阶段 0:构造器做的 4 件事(Web 类型推断、SPI 加载、主类推断)
- 阶段 1-3:BootstrapContext 创建、RunListener 加载、starting 事件
- 阶段 4:Environment 准备(配置文件加载的触发时机)
- 阶段 5-7:Banner 打印、ApplicationContext 创建、prepareContext
- 阶段 8:refreshContext------Spring 核心 refresh() 的 12 个子步骤
- 阶段 9-12:afterRefresh、started、callRunners、ready
- Spring Boot 4.x 启动流程的新变化:模块化自动配置、AOT 模式、Deducer SPI
- 错误处理机制和启动性能分析
一、启动流程全景图
在逐行代码分析之前,先看总览图:
scss
main() 方法
│
├─ new SpringApplication(primarySources) ← 构造函数
│ ├─ deduce WebApplicationType
│ ├─ load BootstrapRegistryInitializer (SPI)
│ ├─ load ApplicationContextInitializer (SPI)
│ ├─ load ApplicationListener (SPI)
│ └─ deduceMainApplicationClass() via StackWalker
│
└─ .run(args) ← 核心启动方法
│
├─ [1] createBootstrapContext() ← 创建引导上下文
├─ [2] getRunListeners(args) ← 获取运行监听器
├─ [3] listeners.starting() ← 发布 starting 事件
├─ [4] prepareEnvironment() ← 准备 Environment
│ ├─ getOrCreateEnvironment()
│ ├─ configureEnvironment() ← configurePropertySources() + configureProfiles()
│ ├─ ConfigurationPropertySources.attach()
│ ├─ listeners.environmentPrepared() ← 发布 environmentPrepared 事件
│ └─ bindToSpringApplication()
│
├─ [5] printBanner() ← 打印 Banner
├─ [6] createApplicationContext() ← 创建 ApplicationContext
├─ [7] prepareContext() ← 准备上下文
│ ├─ setEnvironment()
│ ├─ postProcessApplicationContext()
│ ├─ addAotGeneratedInitializerIfNecessary()
│ ├─ applyInitializers()
│ ├─ listeners.contextPrepared() ← 发布 contextPrepared 事件
│ ├─ bootstrapContext.close()
│ ├─ load(sources) ← 加载主类 BeanDefinition
│ └─ listeners.contextLoaded() ← 发布 contextLoaded 事件
│
├─ [8] refreshContext(context) ← 核心:刷新上下文
│ └─ applicationContext.refresh() ← Spring Framework 的 refresh()
│ ├─ prepareRefresh()
│ ├─ obtainFreshBeanFactory()
│ ├─ prepareBeanFactory()
│ ├─ postProcessBeanFactory()
│ ├─ invokeBeanFactoryPostProcessors() ← 处理自动配置
│ │ └─ ConfigurationClassPostProcessor
│ │ └─ AutoConfigurationImportSelector
│ │ ├─ 加载 META-INF/spring/*.imports
│ │ ├─ 过滤 (OnClassCondition 等)
│ │ └─ 排序 → 生成 @Configuration 类
│ ├─ registerBeanPostProcessors()
│ ├─ initMessageSource()
│ ├─ initApplicationEventMulticaster()
│ ├─ onRefresh() ← 内嵌 Web 服务器启动
│ ├─ registerListeners()
│ ├─ finishBeanFactoryInitialization()
│ └─ finishRefresh()
│
├─ [9] afterRefresh(context, args) ← 空实现,扩展点
├─ [10] listeners.started(context, time) ← 发布 started 事件 + LivenessState.CORRECT
├─ [11] callRunners(context, args) ← 调用 ApplicationRunner / CommandLineRunner
└─ [12] listeners.ready(context, time) ← 发布 ready 事件 + ReadinessState.ACCEPTING_TRAFFIC
下面把每个阶段逐一拆开。
二、阶段 0:SpringApplication 构造器
一切从 SpringApplication.run(MyApp.class, args) 开始:
java
// SpringApplication.java - 静态入口
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
先 new SpringApplication(...),再调用实例方法 run(args)。构造器里做了 4 件关键事:
2.1 推断 Web 应用类型
java
// SpringApplication 构造器 (line 274-284)
public SpringApplication(@Nullable ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "'primarySources' must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.properties.setWebApplicationType(WebApplicationType.deduce()); // ← 推断 Web 类型
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
WebApplicationType.deduce() 的逻辑(WebApplicationType.java:63-71):
java
public static WebApplicationType deduce() {
// 4.0.1 新增:优先通过 SPI 让模块自行推断
for (Deducer deducer : SpringFactoriesLoader.forDefaultResourceLocation().load(Deducer.class)) {
WebApplicationType deduced = deducer.deduceWebApplicationType();
if (deduced != null) {
return deduced;
}
}
// 兜底逻辑:检查 classpath 上是否有 jakarta.servlet.Servlet
// 以及 ConfigurableWebApplicationContext
return isServletApplication() ? WebApplicationType.SERVLET : WebApplicationType.NONE;
}
Spring Boot 4.0.1 引入的 Deducer SPI 让各 Web 模块自主声明应用类型------webmvc 模块的 WebMvcWebApplicationTypeDeducer 返回 SERVLET,webflux 模块的 WebFluxWebApplicationTypeDeducer 返回 REACTIVE------而不是靠硬编码在 WebApplicationType 里检查 DispatcherHandler 类。这是开闭原则的实践:新增 Web 类型只需注册新的 Deducer,不改 WebApplicationType 本身。
两个 Deducer 通过 @Order 保证 MVC 优先(WebMvcWebApplicationTypeDeducer 为 @Order(10),WebFluxWebApplicationTypeDeducer 为 @Order(20))。前者检查 jakarta.servlet.Servlet、org.springframework.web.servlet.DispatcherServlet、ConfigurableWebApplicationContext 三个类,后者检查 reactor.core.publisher.Mono、org.springframework.web.reactive.DispatcherHandler 两个类。
三种结果:
- SERVLET :classpath 上同时存在
jakarta.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext(SERVLET_INDICATOR_CLASSES数组)→ 创建AnnotationConfigServletWebServerApplicationContext(AOT 模式下为ServletWebServerApplicationContext) - REACTIVE :由 WebFlux 模块的
WebFluxWebApplicationTypeDeducer返回 → 创建AnnotationConfigReactiveWebServerApplicationContext(AOT 模式下为ReactiveWebServerApplicationContext) - NONE :都没有 → 创建
AnnotationConfigApplicationContext(AOT 模式下为GenericApplicationContext)
注意类名里都有 WebServer :这些上下文定义在 module/spring-boot-web-server 模块中,由 ServletWebServerApplicationContextFactory / ReactiveWebServerApplicationContextFactory(通过 spring.factories 注册的 ApplicationContextFactory SPI)创建。core/spring-boot 中确实也存在不带 WebServer 的 AnnotationConfigServletWebApplicationContext,但它是 GenericWebApplicationContext 的子类、不管理内嵌服务器,用于外置容器等场景;SpringApplication.run() 的默认流程创建的是带 WebServer 的版本。
2.2 加载 SPI 扩展(spring.factories → SpringFactoriesLoader)
构造器中三次调用 getSpringFactoriesInstances():
java
private <T> List<T> getSpringFactoriesInstances(Class<T> type, @Nullable ArgumentResolver argumentResolver) {
return SpringFactoriesLoader.forDefaultResourceLocation(getClassLoader()).load(type, argumentResolver);
}
在 Spring Boot 4.x 中,SpringFactoriesLoader 读取的是 classpath 下所有 META-INF/spring.factories 文件。这个调用分别加载了:
| SPI 类型 | 用途 | 生命周期 |
|---|---|---|
BootstrapRegistryInitializer |
在引导阶段注册 Bootstrap 实例 | 在 createBootstrapContext() 中执行 |
ApplicationContextInitializer |
在 prepareContext() 阶段初始化 ApplicationContext |
在 applyInitializers() 中执行 |
ApplicationListener |
监听启动全生命周期的各种事件 | 注册到 ApplicationContext 中 |
这些 SPI 扩展点是 Spring Boot 高度可扩展的核心设计。
2.3 推断主类
java
private @Nullable Class<?> deduceMainApplicationClass() {
return StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
.walk(this::findMainClass)
.orElse(null);
}
private Optional<Class<?>> findMainClass(Stream<StackFrame> stack) {
return stack.filter((frame) -> Objects.equals(frame.getMethodName(), "main"))
.findFirst()
.map(StackWalker.StackFrame::getDeclaringClass);
}
Spring Boot 4.x 已经从 new RuntimeException().getStackTrace() 升级到了 JDK 9+ 的 StackWalker API。StackWalker 更高效,因为它不会填充整个栈帧(不需要的帧可以被 walk() 的过滤逻辑跳过)。
三、run() 方法:启动全流程
run() 方法是整个启动流程的指挥中心。核心源码(SpringApplication.java:304-342):
java
public ConfigurableApplicationContext run(String... args) {
Startup startup = Startup.create(); // 计时器
if (this.properties.isRegisterShutdownHook()) {
SpringApplication.shutdownHook.enableShutdownHookAddition(); // 注册 shutdown hook
}
DefaultBootstrapContext bootstrapContext = createBootstrapContext(); // ★ 阶段 1
ConfigurableApplicationContext context = null;
configureHeadlessProperty(); // Headless 模式
SpringApplicationRunListeners listeners = getRunListeners(args); // ★ 阶段 2
listeners.starting(bootstrapContext, this.mainApplicationClass); // ★ 阶段 3
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment( // ★ 阶段 4
listeners, bootstrapContext, applicationArguments);
Banner printedBanner = printBanner(environment); // ★ 阶段 5
context = createApplicationContext(); // ★ 阶段 6
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, // ★ 阶段 7
listeners, applicationArguments, printedBanner);
refreshContext(context); // ★ 阶段 8
afterRefresh(context, applicationArguments); // ★ 阶段 9
Duration timeTakenToStarted = startup.started();
if (this.properties.isLogStartupInfo()) {
new StartupInfoLogger(this.mainApplicationClass, environment)
.logStarted(getApplicationLog(), startup);
}
listeners.started(context, timeTakenToStarted); // ★ 阶段 10
callRunners(context, applicationArguments); // ★ 阶段 11
}
catch (Throwable ex) {
throw handleRunFailure(context, ex, listeners);
}
try {
if (context.isRunning()) {
listeners.ready(context, startup.ready()); // ★ 阶段 12
}
}
catch (Throwable ex) {
throw handleRunFailure(context, ex, null);
}
return context;
}
总共 12 个关键步骤。下面逐阶段深入。
四、阶段 1:createBootstrapContext()
java
private DefaultBootstrapContext createBootstrapContext() {
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
this.bootstrapRegistryInitializers.forEach(
(initializer) -> initializer.initialize(bootstrapContext));
return bootstrapContext;
}
BootstrapContext 并非 Spring Boot 4 的新概念:它自 2.4.0 起就存在------2.4 引入 BootstrapRegistry 的同时就把它拆分成了 BootstrapRegistry(注册)与 BootstrapContext(读取)两个接口。Spring Boot 4.0 做的是把整个 bootstrap 层重构为 org.springframework.boot.bootstrap 包的基础层(BootstrapRegistry 在 4.1.0 中依然存在且未废弃,EnvironmentPostProcessor 等仍在使用)。它的核心作用是:在 ApplicationContext 创建之前,提供一个轻量级的对象注册中心。
与 ApplicationContext 的主要区别:
- 生命周期更早 :BootstrapContext 在
run()的第一步(createBootstrapContext())就已创建,早于 Environment 准备 ------listeners.starting()事件时 Environment 尚不存在 - 作用范围有限 :只用于启动早期的注册,在
prepareContext()完成后就会 close - 典型用途:注册一些需要在 ApplicationContext 之前就初始化的服务,比如配置数据定位器
DefaultBootstrapContext 内部使用 Map<Class<?>, InstanceSupplier<?>> 存储注册信息,支持懒加载单例。
五、阶段 2:getRunListeners(args)
java
private SpringApplicationRunListeners getRunListeners(String[] args) {
ArgumentResolver argumentResolver = ArgumentResolver.of(SpringApplication.class, this);
argumentResolver = argumentResolver.and(String[].class, args);
List<SpringApplicationRunListener> listeners = getSpringFactoriesInstances(
SpringApplicationRunListener.class, argumentResolver);
// 支持 SpringApplicationHook(测试场景)
SpringApplicationHook hook = applicationHook.get();
SpringApplicationRunListener hookListener = (hook != null) ? hook.getRunListener(this) : null;
if (hookListener != null) {
listeners = new ArrayList<>(listeners);
listeners.add(hookListener);
}
return new SpringApplicationRunListeners(logger, listeners, this.applicationStartup);
}
通过 SpringFactoriesLoader 加载 SpringApplicationRunListener 的实现类。在 spring-boot 模块的 META-INF/spring.factories 中注册了:
csharp
# SpringApplicationRunListener
org.springframework.boot.context.event.EventPublishingRunListener
EventPublishingRunListener 是整个启动事件机制的核心。它将 SpringApplicationRunListener 的生命周期回调翻译为 Spring 的 ApplicationEvent,然后广播给所有 ApplicationListener。
EventPublishingRunListener 的事件转换表
| RunListener 回调 | 发布的 ApplicationEvent | 时机 |
|---|---|---|
starting() |
ApplicationStartingEvent |
启动最早 |
environmentPrepared() |
ApplicationEnvironmentPreparedEvent |
Environment 准备完成 |
contextPrepared() |
ApplicationContextInitializedEvent |
Context 创建并初始化后 |
contextLoaded() |
ApplicationPreparedEvent |
Bean 定义加载完成 |
started() |
ApplicationStartedEvent + LivenessState.CORRECT |
refresh 完成 |
ready() |
ApplicationReadyEvent + ReadinessState.ACCEPTING_TRAFFIC |
Runners 执行完 |
failed() |
ApplicationFailedEvent |
启动失败 |
注意 contextLoaded() 中有个关键操作:
java
// EventPublishingRunListener.java:92-100
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener<?> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware contextAware) {
contextAware.setApplicationContext(context);
}
context.addApplicationListener(listener); // ★ 把监听器注册到 ApplicationContext
}
multicastInitialEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}
在 contextLoaded 阶段,构造器中加载的 ApplicationListener 才被正式注册到 ApplicationContext 上。在这之前,事件通过 EventPublishingRunListener 自带的 SimpleApplicationEventMulticaster 来广播。
六、阶段 3:listeners.starting()
java
listeners.starting(bootstrapContext, this.mainApplicationClass);
java
// SpringApplicationRunListeners.java:55-62
void starting(ConfigurableBootstrapContext bootstrapContext, @Nullable Class<?> mainApplicationClass) {
doWithListeners("spring.boot.application.starting",
(listener) -> listener.starting(bootstrapContext),
(step) -> {
if (mainApplicationClass != null) {
step.tag("mainApplicationClass", mainApplicationClass.getName());
}
});
}
SpringApplicationRunListeners 包装了 List<SpringApplicationRunListener>,并且在每次调用时创建 ApplicationStartup 的 StartupStep(用于收集启动性能指标)。
这是第一个启动事件,此时:
- ApplicationContext 还未创建
- Environment 还未准备
- 只有 BootstrapContext 可用
七、阶段 4:prepareEnvironment()
这是最复杂的阶段之一,做了 7 件事:
java
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// 1. 创建或获取 Environment(4.x 中类型由 ApplicationContextFactory 决定)
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 2. 配置 Environment(PropertySources 和 Profiles)
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 3. 附加 ConfigurationPropertySources
ConfigurationPropertySources.attach(environment);
// 4. 发布 environmentPrepared 事件 → 触发 ConfigData 加载!
listeners.environmentPrepared(bootstrapContext, environment);
// 5. 移动 PropertySource 到末尾(保证优先级)
ApplicationInfoPropertySource.moveToEnd(environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
// 6. 校验环境前缀(4.1 新增)+ 绑定 spring.main.* 属性到 SpringApplication
Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
"Environment prefix cannot be set via properties.");
bindToSpringApplication(environment);
// 7. 必要时转换 Environment 类型(如 StandardEnvironment → ApplicationServletEnvironment)
if (!this.isCustomEnvironment) {
EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
7.1 关键步骤 4:发布 environmentPrepared 事件
listeners.environmentPrepared() 会发布 ApplicationEnvironmentPreparedEvent。这个事件是配置文件加载的触发点------spring.factories 中注册的 EnvironmentPostProcessorApplicationListener 收到事件后,会依次调用所有 EnvironmentPostProcessor,其中就包括 ConfigDataEnvironmentPostProcessor,在此阶段加载 application.yml / application.properties 等配置文件。
这也是为什么 EnvironmentPostProcessor 的 SPI 扩展点如此强大:你可以在这个时机插入自定义的配置源。
7.2 Environment 类型转换
在 Spring Boot 4.x 中,Environment 的类型可能和 ApplicationContext 的类型不匹配。例如,你通过 setEnvironment() 传入了一个 StandardEnvironment,但 ApplicationContextFactory 要求 ApplicationServletEnvironment。这时 EnvironmentConverter 会执行类型转换:
java
if (!this.isCustomEnvironment) {
EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
这保证了后续 context.setEnvironment(environment) 时类型兼容。
八、阶段 5-6:打印 Banner + 创建 ApplicationContext
8.1 打印 Banner
java
private @Nullable Banner printBanner(ConfigurableEnvironment environment) {
if (this.properties.getBannerMode(environment) == Banner.Mode.OFF) {
return null;
}
ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
: new DefaultResourceLoader(null);
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
if (this.properties.getBannerMode(environment) == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
打印的 Banner 对象会被注册为单例 Bean(springBootBanner),以便后续通过 @Autowired 获取。
8.2 创建 ApplicationContext
java
protected ConfigurableApplicationContext createApplicationContext() {
ConfigurableApplicationContext context = this.applicationContextFactory
.create(this.properties.getWebApplicationType());
Assert.state(context != null, "ApplicationContextFactory created null context");
return context;
}
默认使用 DefaultApplicationContextFactory,它会通过 SPI 查找匹配的 ApplicationContextFactory 实现:
java
// DefaultApplicationContextFactory.java:57-67
public ConfigurableApplicationContext create(@Nullable WebApplicationType webApplicationType) {
try {
return getFromSpringFactories(webApplicationType, ApplicationContextFactory::create,
this::createDefaultApplicationContext);
}
catch (Exception ex) {
throw new IllegalStateException("Unable create a default ApplicationContext instance, "
+ "you may need a custom ApplicationContextFactory", ex);
}
}
private ConfigurableApplicationContext createDefaultApplicationContext() {
if (!AotDetector.useGeneratedArtifacts()) {
return new AnnotationConfigApplicationContext(); // ← 非 AOT 模式
}
return new GenericApplicationContext(); // ← AOT 模式
}
注意在 AOT 模式下 ,创建的是 GenericApplicationContext,而不是 AnnotationConfigApplicationContext。因为 AOT 编译时已经生成了所有 BeanDefinition,运行时不再需要注解扫描。
另外,上面的 createDefaultApplicationContext() 只是 NONE 类型的兜底 。对于 SERVLET / REACTIVE 类型,DefaultApplicationContextFactory 通过 SPI 找到 module/spring-boot-web-server 注册的 ServletWebServerApplicationContextFactory / ReactiveWebServerApplicationContextFactory,分别创建 AnnotationConfigServletWebServerApplicationContext 和 AnnotationConfigReactiveWebServerApplicationContext(AOT 模式下为不带 AnnotationConfig 前缀的 ServletWebServerApplicationContext / ReactiveWebServerApplicationContext)。
九、阶段 7:prepareContext() --- 准备上下文
这是 refresh() 之前最关键的准备工作:
java
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, @Nullable Banner printedBanner) {
// 1. 设置 Environment
context.setEnvironment(environment);
// 2. 后处理 ApplicationContext
postProcessApplicationContext(context);
// 3. 处理 AOT 初始化器
addAotGeneratedInitializerIfNecessary(this.initializers);
// 4. 配置 BeanFactory 行为
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
if (beanFactory instanceof AbstractAutowireCapableBeanFactory autowireCapableBeanFactory) {
autowireCapableBeanFactory.setAllowCircularReferences(this.properties.isAllowCircularReferences());
if (beanFactory instanceof DefaultListableBeanFactory listableBeanFactory) {
listableBeanFactory.setAllowBeanDefinitionOverriding(
this.properties.isAllowBeanDefinitionOverriding());
}
}
// 5. 执行 ApplicationContextInitializer
applyInitializers(context);
// 6. 发布 contextPrepared 事件
listeners.contextPrepared(context);
// 7. 关闭 BootstrapContext
bootstrapContext.close(context);
// 8. 注册 Spring Boot 特定单例 Bean
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
// 9. 加载 sources(主类及其注解引入的配置类)
if (!AotDetector.useGeneratedArtifacts()) {
Set<Object> sources = getAllSources();
Assert.state(!ObjectUtils.isEmpty(sources), "No sources defined");
load(context, sources.toArray(new Object[0]));
}
// 10. 发布 contextLoaded 事件
listeners.contextLoaded(context);
}
注:为突出重点,上面的代码省略了 4 个非核心分支------isLogStartupInfo() 时的 logStartupInfo() / logStartupProfileInfo() 日志输出、spring.main.lazy-initialization 开启时注册 LazyInitializationBeanFactoryPostProcessor、spring.main.keep-alive 开启时注册 KeepAlive 监听器,以及无条件注册的 PropertySourceOrderingBeanFactoryPostProcessor(保证 PropertySource 顺序)。这些分支的实际位置在 bootstrapContext.close(context) 与 registerSingleton 之间。
9.1 AOT 模式下的特殊处理
java
private void addAotGeneratedInitializerIfNecessary(List<ApplicationContextInitializer<?>> initializers) {
if (AotDetector.useGeneratedArtifacts()) {
// 查找已有的 AotApplicationContextInitializer
List<ApplicationContextInitializer<?>> aotInitializers = new ArrayList<>(
initializers.stream().filter(AotApplicationContextInitializer.class::isInstance).toList());
if (aotInitializers.isEmpty()) {
// 找不到就用约定的类名:{MainClass}__ApplicationContextInitializer
String initializerClassName = this.mainApplicationClass.getName()
+ "__ApplicationContextInitializer";
if (!ClassUtils.isPresent(initializerClassName, getClassLoader())) {
throw new AotInitializerNotFoundException(this.mainApplicationClass, initializerClassName);
}
aotInitializers.add(AotApplicationContextInitializer.forInitializerClasses(initializerClassName));
}
// 把 AOT 初始化器放到最前面执行
initializers.removeAll(aotInitializers);
initializers.addAll(0, aotInitializers);
}
// Native Image 环境检查(要求 JDK 25+)
if (NativeDetector.inNativeImage()) {
NativeImageRequirementsException.throwIfNotMet();
}
}
在 AOT 模式下,Spring Boot 会:
- 优先查找实现
AotApplicationContextInitializer接口的初始化器 - 如果找不到,就用约定类名
{MainClass}__ApplicationContextInitializer加载(由 AOT 编译插件生成的类) - 把这个初始化器放到列表最前面,确保它最先执行
- 跳过
load(sources)步骤------因为 AOT 已经把所有 BeanDefinition 生成好了
9.2 load() --- 加载主类
java
protected void load(ApplicationContext context, Object[] sources) {
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
loader.load();
}
BeanDefinitionLoader 是一个组合加载器,它根据 source 的类型选择加载方式:
- Class 类型 →
AnnotatedBeanDefinitionReader注册(包括@Configuration类) - XML 资源路径 →
XmlBeanDefinitionReader加载 - 包名 →
ClassPathBeanDefinitionScanner扫描 - Groovy 脚本 →
GroovyBeanDefinitionReader加载
通常我们传入的主类带有 @SpringBootApplication 注解(它包含了 @Configuration 和 @EnableAutoConfiguration),所以会通过 AnnotatedBeanDefinitionReader 注册,从而触发后续的自动配置导入流程。
十、阶段 8:refreshContext() --- 核心
java
private void refreshContext(ConfigurableApplicationContext context) {
if (this.properties.isRegisterShutdownHook()) {
shutdownHook.registerApplicationContext(context);
}
refresh(context);
}
protected void refresh(ConfigurableApplicationContext applicationContext) {
applicationContext.refresh(); // ← 委托给 Spring Framework
}
applicationContext.refresh() 调用的是 AbstractApplicationContext.refresh(),这是 Spring Framework 的模板方法,包含 12 个子步骤(Spring Framework 7.0 中已用 ReentrantLock 取代了 6.x 时期的 synchronized (startupShutdownMonitor)):
java
// AbstractApplicationContext.refresh() - Spring Framework 7.0
public void refresh() throws BeansException, IllegalStateException {
this.startupShutdownLock.lock();
try {
this.startupShutdownThread = Thread.currentThread();
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// 1. 准备刷新
prepareRefresh();
// 2. 获取 BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. 准备 BeanFactory
prepareBeanFactory(beanFactory);
try {
// 4. BeanFactory 后处理(空实现,留给子类扩展)
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// 5. ★★★ 调用 BeanFactoryPostProcessor ★★★
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 注册 BeanPostProcessor
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// 7. 初始化 MessageSource(国际化)
initMessageSource();
// 8. 初始化事件广播器
initApplicationEventMulticaster();
// 9. 留给子类的扩展点(内嵌 Web 服务器在这里启动!)
onRefresh();
// 10. 注册 ApplicationListener
registerListeners();
// 11. ★ 实例化所有非懒加载的单例 Bean ★
finishBeanFactoryInitialization(beanFactory);
// 12. 完成刷新(清理缓存、发布 ContextRefreshedEvent)
finishRefresh();
}
catch (RuntimeException | Error ex) {
// 7.0:先停止已启动的 Lifecycle bean,再销毁单例、取消刷新
if (this.lifecycleProcessor != null && this.lifecycleProcessor.isRunning()) {
this.lifecycleProcessor.stop();
}
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
contextRefresh.end();
}
}
finally {
this.startupShutdownThread = null;
this.startupShutdownLock.unlock();
}
}
10.1 步骤 5:invokeBeanFactoryPostProcessors() --- 自动配置的入口
这是 Spring Boot 启动中关键的一步。BeanFactoryPostProcessor 可以在 BeanDefinition 加载之后、Bean 实例化之前修改 BeanDefinition。
最核心的 BeanFactoryPostProcessor 是 ConfigurationClassPostProcessor,它负责处理所有 @Configuration 类。而 @SpringBootApplication 中的 @EnableAutoConfiguration 引入了 AutoConfigurationImportSelector,它是一个 DeferredImportSelector:
自动配置的加载链路:
less
@SpringBootApplication
→ @EnableAutoConfiguration
→ @Import(AutoConfigurationImportSelector.class)
→ AutoConfigurationImportSelector.selectImports()
→ getAutoConfigurationEntry()
1. getCandidateConfigurations()
→ ImportCandidates.load(AutoConfiguration.class)
→ 读取所有 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
(Spring Boot 4.x 不再使用 spring.factories 来加载自动配置类!)
2. removeDuplicates()
3. getExclusions() - 处理 @EnableAutoConfiguration(exclude=...) 和 spring.autoconfigure.exclude
4. getConfigurationClassFilter().filter() ★
→ 执行 AutoConfigurationImportFilter(OnClassCondition、OnBeanCondition、OnWebApplicationCondition)
过滤不满足条件的自动配置类
5. fireAutoConfigurationImportEvents()
→ 返回符合条件的自动配置类列表
关键变化:从 spring.factories 到 *.imports 文件
在 Spring Boot 3.x 及之前,自动配置类通过 META-INF/spring.factories 中的 org.springframework.boot.autoconfigure.EnableAutoConfiguration 键来注册。
从 Spring Boot 3.0 开始,迁移到了 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件。在 Spring Boot 4.x 中,这个迁移已经完成。
每个模块都有自己的 .imports 文件,例如 spring-boot-autoconfigure 模块的:
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration
org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration
其他模块(jdbc、webmvc、jackson、grpc 等)也有各自的 .imports 文件。
条件过滤
getConfigurationClassFilter().filter(configurations) 通过 OnClassCondition、OnBeanCondition、OnWebApplicationCondition 三个 Filter 来过滤:
- OnClassCondition :检查
@ConditionalOnClass/@ConditionalOnMissingClass指定的类是否存在 - OnBeanCondition :检查
@ConditionalOnBean/@ConditionalOnMissingBean指定的 Bean 是否存在 - OnWebApplicationCondition :检查
@ConditionalOnWebApplication/@ConditionalOnNotWebApplication
注意:与自动配置类走 .imports 文件不同,这三个 Filter 在 4.x 中仍通过 spring-boot-autoconfigure 模块 META-INF/spring.factories 中的 AutoConfigurationImportFilter 键注册,由 AutoConfigurationImportSelector.getAutoConfigurationImportFilters() 用 SpringFactoriesLoader 加载。
过滤结果会记录在 ConditionEvaluationReport 中,你可以通过 --debug 启动参数看到详细的"条件评估报告"。
10.2 步骤 9:onRefresh() --- Web 服务器启动
onRefresh() 是 AbstractApplicationContext 留给子类的模板方法。在 Servlet Web 环境下:
scss
ServletWebServerApplicationContext.onRefresh()
→ createWebServer()
→ WebServerFactory.getWebServer()
→ TomcatServletWebServerFactory.getWebServer()
→ 创建并启动 Tomcat
所以在 refresh() 过程中,内嵌的 Tomcat/Jetty/Undertow 就已经启动了。在 Spring Boot 4.1 中,createWebServer() 还会额外注册一个名为 webServerStartStop 的单例 Bean(WebServerStartStopLifecycle,一个 SmartLifecycle),用于统一协调内嵌服务器的后续启动/停止/暂停生命周期;而 Tomcat 的实际启动动作仍发生在 TomcatWebServer 构造时的 initialize() 中(即 onRefresh 阶段)。
10.3 步骤 11:finishBeanFactoryInitialization() --- 实例化所有单例 Bean
这里实例化所有非懒加载的单例 Bean。包括:
- 你定义的
@Service、@Repository、@Controller - 自动配置注册的各种 Bean(DataSource、JdbcTemplate、RestClient 等)
@Configuration类中@Bean方法定义的 Bean
这一步通常是启动过程中最耗时的环节。
十一、阶段 9-12:afterRefresh → started → callRunners → ready
11.1 afterRefresh()
java
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
这是一个空的模板方法,留给子类重写。在 Spring Boot 自身中没有使用,但你可以继承 SpringApplication 来实现自己的启动后逻辑。
11.2 listeners.started() --- 应用启动完成
java
listeners.started(context, timeTakenToStarted);
EventPublishingRunListener 接收这个回调后做两件事:
java
@Override
public void started(ConfigurableApplicationContext context, @Nullable Duration timeTaken) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context, timeTaken));
AvailabilityChangeEvent.publish(context, LivenessState.CORRECT); // ← 标记应用为"存活"
}
ApplicationStartedEvent:通知所有监听器"应用已启动"LivenessState.CORRECT:告诉 Kubernetes 的 Liveness Probe"应用还活着"
11.3 callRunners() --- 执行 Runner
java
private void callRunners(ConfigurableApplicationContext context, ApplicationArguments args) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
String[] beanNames = beanFactory.getBeanNamesForType(Runner.class);
Map<Runner, String> instancesToBeanNames = new IdentityHashMap<>();
for (String beanName : beanNames) {
instancesToBeanNames.put(beanFactory.getBean(beanName, Runner.class), beanName);
}
Comparator<Object> comparator = getOrderComparator(beanFactory)
.withSourceProvider(new FactoryAwareOrderSourceProvider(beanFactory, instancesToBeanNames));
instancesToBeanNames.keySet().stream().sorted(comparator).forEach((runner) -> callRunner(runner, args));
}
注意两个细节:
- Runner 接口 :Spring Boot 内部定义了一个
Runner接口(包级私有),ApplicationRunner和CommandLineRunner都实现了它 - 排序执行 :使用
OrderComparator排序后执行。ApplicationRunner和CommandLineRunner的区别在于参数:ApplicationRunner.run(ApplicationArguments args)--- 封装后的参数CommandLineRunner.run(String... args)--- 原始String[]
11.4 listeners.ready() --- 应用完全就绪
java
try {
if (context.isRunning()) {
listeners.ready(context, startup.ready());
}
}
catch (Throwable ex) {
throw handleRunFailure(context, ex, null);
}
与 started 不同,ready 位于独立的第二个 try-catch 块 中。这意味着如果 callRunners() 抛出了异常(会被第一个 catch 捕获并交给 handleRunFailure),ready 事件根本不会执行。
java
@Override
public void ready(ConfigurableApplicationContext context, @Nullable Duration timeTaken) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context, timeTaken));
AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC); // ← 标记为"可接受流量"
}
ApplicationReadyEvent:通知所有监听器"应用完全就绪"ReadinessState.ACCEPTING_TRAFFIC:告诉 Kubernetes 的 Readiness Probe"可以开始接收请求了"
十二、Spring Boot 4.x 启动流程的新变化
12.1 模块化自动配置(4.0)
在 Spring Boot 3.x 中,spring-boot-autoconfigure 是一个巨大的模块,包含了所有的自动配置类。Spring Boot 4.0 将自动配置拆分到了各个独立模块中:
arduino
spring-boot-autoconfigure/ ← 核心自动配置(仅 12 个类)
module/spring-boot-jdbc/ ← JDBC/Datasource 自动配置
module/spring-boot-webmvc/ ← WebMVC 自动配置
module/spring-boot-tomcat/ ← Tomcat 自动配置
module/spring-boot-jackson/ ← Jackson 自动配置
module/spring-boot-grpc-server/ ← gRPC Server 自动配置 (4.1 新增)
module/spring-boot-grpc-client/ ← gRPC Client 自动配置 (4.1 新增)
...
每个模块都有自己的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,这样只有引入的 starter 才会触发相应的自动配置,减小了 classpath 扫描范围。
12.2 AOT 模式下的启动差异
在 AOT 编译模式下,启动流程与正常模式有本质区别:
| 步骤 | 正常模式 | AOT 模式 |
|---|---|---|
| ApplicationContext 类型(NONE 默认) | AnnotationConfigApplicationContext |
GenericApplicationContext |
| ApplicationContext 类型(SERVLET / REACTIVE) | AnnotationConfigServletWebServerApplicationContext / AnnotationConfigReactiveWebServerApplicationContext |
ServletWebServerApplicationContext / ReactiveWebServerApplicationContext |
| 初始化器 | SPI 加载的 ApplicationContextInitializer |
生成的 {Main}__ApplicationContextInitializer |
| Bean 定义加载 | load(sources) → 注解扫描 |
跳过(AOT 已生成) |
| 条件评估 | 运行时执行 @Conditional |
构建期已评估,生成的代码中已过滤 |
| 反射/代理 | 运行时动态代理 | 替换为静态代码/直接实例化 |
SpringApplicationAotProcessor 就是 AOT 编译的入口(官方文档确认)。构建插件(Maven/Gradle)调用它来生成 __ApplicationContextInitializer 等类;运行时通过 -Dspring.aot.enabled=true 系统属性(AotDetector.useGeneratedArtifacts() 检测)切换到 AOT 模式启动。
12.3 StackWalker 替代 getStackTrace()
Spring Boot 4.x 用 StackWalker(JDK 9+)替代了之前的 new RuntimeException().getStackTrace()。它不仅更高效,还能利用 walk() 的流式 API 避免遍历整个调用栈。
12.4 WebApplicationType.Deducer SPI(4.0.1)
不再硬编码 Web 类型检测逻辑。Web MVC 模块(WebMvcWebApplicationTypeDeducer → SERVLET)和 WebFlux 模块(WebFluxWebApplicationTypeDeducer → REACTIVE)通过 WebApplicationType.Deducer SPI 自主声明环境类型,更好地支持了模块化架构。
十三、错误处理机制
java
catch (Throwable ex) {
throw handleRunFailure(context, ex, listeners);
}
handleRunFailure() 的执行流程:
- 短路检查 :如果异常是
AbandonedRunException(测试场景中用于主动中止启动),直接原样返回,不执行任何清理 - 处理退出码 :如果异常映射到退出码(通过
ExitCodeExceptionMapperBean),记录退出码 - 发布 failed 事件 :
listeners.failed(context, exception)→ApplicationFailedEvent - 报告异常 :通过
SpringBootExceptionReporter报告(SPI 加载),兜底用logger.error(与 4、5 一起在finally中执行) - 关闭 ApplicationContext :
context.close() - 注销 ShutdownHook :
shutdownHook.deregisterFailedApplicationContext(context)
十四、启动性能分析
Spring Boot 4.1.0 内置了启动性能度量支持。ApplicationStartup 接口及其实现 FlightRecorderApplicationStartup 可以集成 JFR(JDK Flight Recorder)来收集启动指标:
java
// 在 SpringApplicationRunListeners 中,每个阶段都有 StartupStep
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
@Nullable Consumer<StartupStep> stepAction) {
StartupStep step = this.applicationStartup.start(stepName);
this.listeners.forEach(listenerAction);
if (stepAction != null) {
stepAction.accept(step);
}
step.end();
}
启动过程中会记录的 step 名称包括:
spring.boot.application.startingspring.boot.application.environment-preparedspring.boot.application.context-preparedspring.boot.application.context-loadedspring.boot.application.startedspring.boot.application.ready
在 Spring Boot 3.x 中可以通过 spring.application.startup=flightrecorder 属性启用,但 Spring Boot 4.x 已移除该属性 (ApplicationProperties 中不再有 startup 字段)。4.x 需要用编程方式启用:springApplication.setApplicationStartup(new FlightRecorderApplicationStartup())(该类来自 Spring Framework 的 org.springframework.core.metrics.jfr 包),之后便可在 JFR 中分析每个阶段的耗时。
十五、总结
Spring Boot 4.1.0 的启动流程可以总结为**"一个构造器 + 十二个阶段"**:
| 阶段 | 方法 | 核心工作 |
|---|---|---|
| 0 | new SpringApplication() |
推断 Web 类型、加载 SPI 扩展、推断主类 |
| 1 | createBootstrapContext() |
创建引导上下文、执行 BootstrapRegistryInitializer |
| 2 | getRunListeners() |
加载 SpringApplicationRunListener(EventPublishingRunListener) |
| 3 | listeners.starting() |
发布 ApplicationStartingEvent |
| 4 | prepareEnvironment() |
创建 Environment、加载配置文件(触发生命周期事件) |
| 5 | printBanner() |
打印 Banner |
| 6 | createApplicationContext() |
创建对应的 ApplicationContext(Servlet/Reactive/None) |
| 7 | prepareContext() |
执行初始器、加载主类、发布 contextLoaded |
| 8 | refreshContext() |
Spring 核心 refresh():BFPP → 自动配置 → Web 服务器 → 单例实例化 |
| 9 | afterRefresh() |
空扩展点 |
| 10 | listeners.started() |
发布 ApplicationStartedEvent + LivenessState |
| 11 | callRunners() |
执行 ApplicationRunner / CommandLineRunner |
| 12 | listeners.ready() |
发布 ApplicationReadyEvent + ReadinessState |
三个最重要的扩展点:
ApplicationContextInitializer:在 refresh 之前定制 ApplicationContextSpringApplicationRunListener:监听启动全生命周期的每个阶段ApplicationRunner / CommandLineRunner:在应用就绪后执行业务逻辑
掌握这个流程,排查启动性能、定制启动行为、排查自动配置失效问题,都能做到心中有数。