万字长文剖析 Spring Boot 4.1.0 启动流程源码:从 main 到就绪

本文是 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.Servletorg.springframework.web.servlet.DispatcherServletConfigurableWebApplicationContext 三个类,后者检查 reactor.core.publisher.Monoorg.springframework.web.reactive.DispatcherHandler 两个类。

三种结果:

  • SERVLET :classpath 上同时存在 jakarta.servlet.Servletorg.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 中确实也存在不带 WebServerAnnotationConfigServletWebApplicationContext,但它是 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>,并且在每次调用时创建 ApplicationStartupStartupStep(用于收集启动性能指标)。

这是第一个启动事件,此时:

  • 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) 时类型兼容。


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,分别创建 AnnotationConfigServletWebServerApplicationContextAnnotationConfigReactiveWebServerApplicationContext(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 开启时注册 LazyInitializationBeanFactoryPostProcessorspring.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 会:

  1. 优先查找实现 AotApplicationContextInitializer 接口的初始化器
  2. 如果找不到,就用约定类名 {MainClass}__ApplicationContextInitializer 加载(由 AOT 编译插件生成的类)
  3. 把这个初始化器放到列表最前面,确保它最先执行
  4. 跳过 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。

最核心的 BeanFactoryPostProcessorConfigurationClassPostProcessor,它负责处理所有 @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) 通过 OnClassConditionOnBeanConditionOnWebApplicationCondition 三个 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));
}

注意两个细节:

  1. Runner 接口 :Spring Boot 内部定义了一个 Runner 接口(包级私有),ApplicationRunnerCommandLineRunner 都实现了它
  2. 排序执行 :使用 OrderComparator 排序后执行。ApplicationRunnerCommandLineRunner 的区别在于参数:
    • 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 模块(WebMvcWebApplicationTypeDeducerSERVLET)和 WebFlux 模块(WebFluxWebApplicationTypeDeducerREACTIVE)通过 WebApplicationType.Deducer SPI 自主声明环境类型,更好地支持了模块化架构。


十三、错误处理机制

java 复制代码
catch (Throwable ex) {
    throw handleRunFailure(context, ex, listeners);
}

handleRunFailure() 的执行流程:

  1. 短路检查 :如果异常是 AbandonedRunException(测试场景中用于主动中止启动),直接原样返回,不执行任何清理
  2. 处理退出码 :如果异常映射到退出码(通过 ExitCodeExceptionMapper Bean),记录退出码
  3. 发布 failed 事件 :listeners.failed(context, exception)ApplicationFailedEvent
  4. 报告异常 :通过 SpringBootExceptionReporter 报告(SPI 加载),兜底用 logger.error(与 4、5 一起在 finally 中执行)
  5. 关闭 ApplicationContext :context.close()
  6. 注销 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.starting
  • spring.boot.application.environment-prepared
  • spring.boot.application.context-prepared
  • spring.boot.application.context-loaded
  • spring.boot.application.started
  • spring.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 之前定制 ApplicationContext
  • SpringApplicationRunListener:监听启动全生命周期的每个阶段
  • ApplicationRunner / CommandLineRunner:在应用就绪后执行业务逻辑

掌握这个流程,排查启动性能、定制启动行为、排查自动配置失效问题,都能做到心中有数。


相关推荐
2602_959960921 小时前
电商大厂Java面试:从Spring Boot、JPA、微服务到Redis、Kafka、Spring Security与监控,谢飞机的爆笑三轮问答
java·jvm·spring boot·redis·面试题
码农进化录1 小时前
Java 程序员的 AI 进化论 | Spring Boot 搭企业智能客服,从设计到上线
java·spring boot·openai
旺仔学长 哈哈1 小时前
别只做车辆 CRUD:Spring Boot 共享汽车管理系统从预约到还车的完整实现---源码53766
java·spring boot·在线预约·共享汽车
weixin_BYSJ19871 小时前
springboot技能与工具共享小程序---附源码29657
java·javascript·spring boot·python·小程序·django·php
leoZ2311 小时前
Vue3 还原一个企业级后台-14-项目总结
开发语言·人工智能·后端·opencv·计算机视觉·数据挖掘·rust
茶本无香1 小时前
通用报表自动化框架:Java调用Shell传参执行PostgreSQL SQL模板
java·sql·postgresql·shell
坤岭1 小时前
企业级Agent从0到1
后端
weixin_BYSJ19871 小时前
flask民族服饰饰品商城小程序---附源码37399
java·javascript·spring boot·python·小程序·django·php
爱笑的源码基地2 小时前
智慧工地源码, 智慧工地环境监测系统架构与绿色施工闭环设计
java·源码·智慧工地·绿色工地