SpringBoot 启动流程全解析:从源码到面试,一文打通 Bean 生命周期与自动配置

本文将按照真实源码的执行顺序,把 SpringBoot 启动流程、Bean 生命周期、三级缓存、自动配置、SPI 机制、BeanPostProcessor 等核心知识点全部串联起来。无论是应对面试,还是日常排查启动报错,这篇文章都能为你提供清晰的思路。


整体总览:SpringBoot 启动的两大阶段

SpringBoot 的启动过程本质上可以分为两大阶段:

  1. SpringApplication 实例化阶段:构造器初始化,准备启动所需的基础组件。
  2. run() 方法执行阶段:真正启动容器、加载 Bean、启动 Web 服务。

入口代码永远是这一行:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

全局启动流程图

main方法启动

第一阶段: new SpringApplication

推断应用类型 Web/None

加载 ApplicationContextInitializer

加载 ApplicationListener

推断主类 MainApplicationClass

第二阶段: 执行 run 方法

启动计时器 & 获取监听器

准备 Environment 环境

打印 Banner

创建 ApplicationContext 容器

预处理上下文

核心: refresh 刷新容器

刷新后处理 & 停止计时

执行 Runner 回调

发布启动完成事件


第一阶段:SpringApplication 实例化

当你调用 run 方法时,底层会先 new 一个 SpringApplication 对象。构造器主要做 4 件事:

1. 判断应用类型(WebApplicationType)

Spring 会根据类路径下是否存在特定的类来判断应用类型:

  • SERVLET:存在 ServletDispatcherServlet 等类,判定为普通 Web 应用(Spring MVC)。
  • REACTIVE:存在 WebFlux 相关类(如 DispatcherHandler),且不存在 Servlet 类,判定为响应式应用。
  • NONE:无 Web 环境相关类,判定为纯后台应用(如定时任务、消息消费者)。

2. 加载并设置 ApplicationContextInitializer(容器初始化器)

通过 Spring 的 SPI 机制,扫描所有 jar 包下的配置文件,找到 key 为 ApplicationContextInitializer 的实现类并保存。

原理解析:在 Spring Boot 2.7 之前,扫描的是 META-INF/spring.factories;在 Spring Boot 2.7 及 3.x 版本中,自动配置相关的 SPI 文件改为了 META-INF/spring/org.springframework.boot.context.ApplicationContextInitializer.imports。这些初始化器会在容器刷新(refresh)之前被调用,用于对容器进行自定义扩展。

3. 加载并设置 ApplicationListener(事件监听器)

同样通过 SPI 机制加载所有事件监听器。Spring 内部定义了一系列事件(如 ApplicationStartingEventApplicationReadyEvent),这些监听器用于在启动的各个节点执行自定义逻辑。

4. 推断主类(MainApplicationClass)

通过获取当前线程的堆栈追踪(Stack Trace),找到执行 main 方法的类,也就是你的启动类。这为后续扫描 @SpringBootApplication 提供了基准包路径。


第二阶段:执行 run() 方法(核心启动流程)

这是真正的启动过程,按源码执行顺序拆解为 12 个关键步骤。

步骤 1:启动计时器与获取启动监听器

StopWatch stopWatch = new StopWatch();
stopWatch.start();
  • 记录启动耗时。
  • 通过 SPI 获取 SpringApplicationRunListener 实现(默认是 EventPublishingRunListener)。
  • 发布 ApplicationStartingEvent(应用开始启动事件)。

步骤 2:准备应用环境 Environment

这一步加载所有配置,是整个应用的配置来源。加载顺序决定了配置的优先级(优先级从高到低):

  1. 命令行参数(--server.port=8080
  2. Java 系统属性(System.getProperties()
  3. 操作系统环境变量(System.getenv()
  4. 配置文件(application.properties / application.yml,以及 application-{profile}.yml

绑定配置到 Spring Environment 对象后,发布 ApplicationEnvironmentPreparedEvent(环境准备完成事件)。

步骤 3:打印 Banner

读取项目根目录下的 banner.txt 或默认的 Spring Boot Banner 并打印。无实际业务功能,纯标识作用。

步骤 4:创建 Spring 容器 ApplicationContext

根据步骤 1 判断的应用类型,创建对应的 IOC 容器:

  • Servlet Web 应用:创建 AnnotationConfigServletWebServerApplicationContext
  • 非 Web 应用:创建 AnnotationConfigApplicationContext

原理解析:Web 容器继承自非 Web 容器,额外增加了对 Servlet 环境(如 ServletContext)的支持,并能够管理内嵌 Tomcat 的生命周期。

步骤 5:预处理上下文(前置准备)

给刚创建的空容器做初始化:

  1. Environment 绑定到容器。
  2. 执行第一阶段加载的所有 ApplicationContextInitializer
  3. 发布 ApplicationContextInitializedEvent
  4. 将启动类(Main Class)作为配置类注册到容器中。

步骤 6:核心中的核心 —— refresh(context) 刷新容器

这一步是 Spring 容器的灵魂。refresh()AbstractApplicationContext 的模板方法,内部包含 12 个子步骤。Bean 扫描、自动配置、实例化、AOP 全在这里执行。

refresh() 内部 12 步拆解

① prepareRefresh()

  • 初始化容器状态(标记为 active)。
  • 验证 Environment 中配置的必选属性(通过 @Value("${xxx:#{null}}"environment.setRequiredProperties() 设置)是否存在。

② obtainFreshBeanFactory()

  • 刷新内部 BeanFactory
  • 创建 DefaultListableBeanFactory,这是 Spring IOC 的底层核心实现,后续所有的 Bean 定义和实例都存放在这里。

③ prepareBeanFactory(beanFactory)

  • 设置类加载器(ClassLoader)。
  • 添加核心的 BeanPostProcessorApplicationContextAwareProcessor(用于处理实现了 ApplicationContextAware 等 Aware 接口的 Bean)。
  • 注册默认的环境 Bean(如 environmentsystemProperties)。

④ postProcessBeanFactory(beanFactory)

  • 空方法,留给子类扩展。
  • Web 应用会在这里注册 ServletContextAwareProcessor,处理 Web 相关的 Aware 接口。

⑤ invokeBeanFactoryPostProcessors(beanFactory) 【自动配置核心】
这是自动配置和 Bean 扫描的关键执行点:

  1. 执行内置的 ConfigurationClassPostProcessor(实现了 BeanDefinitionRegistryPostProcessor)。
  2. 解析启动类上的 @SpringBootApplication,进而解析 @ComponentScan,扫描包下所有的 @Component/@Service/@Controller
  3. 解析 @Configuration@Bean 配置类。
  4. 自动配置加载:通过 AutoConfigurationImportSelector 加载 @EnableAutoConfiguration 引入的所有自动配置类。
  5. 根据 @Conditional(如 @ConditionalOnClass)条件判断是否将配置类注册为 Bean。
  6. 将所有解析出的类转化为 BeanDefinition,注册到 BeanFactory 中。

概念补充:什么是 BeanDefinition?
它不是 Bean 实例,而是 Bean 的“元数据”或“图纸”。它记录了 Bean 的类名、作用域、是否懒加载、依赖关系等信息。Spring 后续会根据这些图纸来实例化 Bean。

⑥ registerBeanPostProcessors(beanFactory)

  • 扫描并注册所有的 BeanPostProcessor(Bean 后置处理器)。
  • 包括处理 @AutowiredAutowiredAnnotationBeanPostProcessor,处理 AOP 的 AnnotationAwareAspectJAutoProxyCreator 等。
  • 注意区分BeanFactoryPostProcessor 作用于 Bean 的定义阶段(修改图纸),而 BeanPostProcessor 作用于 Bean 的实例化阶段(修改成品)。

⑦ initMessageSource()

  • 初始化国际化资源(i18n),用于多语言支持。

⑧ initApplicationEventMulticaster()

  • 初始化事件广播器。后续容器中发布的所有事件,都由这个广播器分发给对应的 Listener。

⑨ onRefresh() 【内嵌 Tomcat 启动】

  • 空方法,留给子类扩展。
  • 对于 Web 应用,ServletWebServerApplicationContext 会重写此方法,在这里创建并启动内嵌的 Tomcat/Jetty/Undertow,并初始化 DispatcherServlet

⑩ registerListeners()

  • 将所有 ApplicationListener 注册到步骤 8 创建的事件广播器中。

⑪ finishBeanFactoryInitialization(beanFactory) 【Bean 实例化核心】
这一步实例化所有非懒加载的单例 Bean,你学过的所有 Bean 原理都在这里:

  1. 遍历所有 BeanDefinition
  2. 调用 getBean() 开始创建 Bean。
  3. 执行 Bean 完整生命周期(详见下方生命周期图)。
  4. 利用三级缓存解决循环依赖。
  5. 生成 AOP 代理对象。
  6. 所有成品 Bean 存入 singletonObjects(一级缓存/单例池)。

⑫ finishRefresh()

  • 清除上下文缓存。
  • 初始化生命周期处理器(LifecycleProcessor)。
  • 发布 ContextRefreshedEvent(容器刷新完成事件)。
  • 正式发布 Web 服务(Tomcat 开始接收外部请求)。

Bean 生命周期与三级缓存时序图

为了更清晰地理解步骤 11 中的 Bean 创建过程,请参考以下时序图:

BeanPostProcessor 目标 Bean BeanFactory Spring 容器 BeanPostProcessor 目标 Bean BeanFactory Spring 容器 若发现循环依赖,从三级缓存获取早期引用, 并移入二级缓存 (earlySingletonObjects) AOP 代理在此阶段生成 getBean("targetBean") 1. 实例化 Bean (调用构造器) 2. 将 ObjectFactory 放入三级缓存 (singletonFactories) 3. 属性填充 (populateBean) 4. 调用 Aware 接口方法 (BeanNameAware 等) 5. 初始化前 (postProcessBeforeInitialization) 返回处理后的 Bean 6. 执行初始化方法 (@PostConstruct, InitializingBean, init-method) 7. 初始化后 (postProcessAfterInitialization) 返回代理对象或原对象 8. 将最终成品放入一级缓存 (singletonObjects) 9. 清理二级、三级缓存 返回最终的 Bean 实例

步骤 7:刷新后处理 postProcessAfterRefresh()

默认空实现,留给子类在容器刷新完成后进行自定义扩展。

步骤 8:停止计时器,打印启动耗时

Started Application in 1.53 seconds (JVM running for 2.1)

步骤 9:执行 ApplicationRunner 与 CommandLineRunner

所有实现了这两个接口的 Bean,会在容器完全启动后、接收外部请求前执行。常用于项目启动后加载字典数据、初始化缓存、校验配置等。

  • 执行顺序:可以通过 @Order 注解控制。
  • 区别CommandLineRunner 接收原始的 String[] args 参数;ApplicationRunner 接收封装后的 ApplicationArguments 对象,支持解析 --key=value 格式的参数。

步骤 10:发布 ApplicationStartedEvent

发布应用已启动成功事件,通知所有监听器。

步骤 11:检查是否有异常

如果启动过程中捕获到异常,发布 ApplicationFailedEvent,并抛出异常终止应用。

步骤 12:返回就绪的 ApplicationContext

启动完成,返回 IOC 容器,应用正式对外提供服务。


核心知识点对应关系表

为了帮你把零散的知识点串联起来,请参考下表:

启动步骤 对应核心原理 / 面试考点
构造器初始化 Spring SPI 机制(spring.factories / .imports)、应用类型推断
prepareEnvironment 配置加载优先级、多环境配置(Profiles)原理
invokeBeanFactoryPostProcessors 自动配置原理(@EnableAutoConfiguration)、@Conditional 条件注解、BeanDefinition 概念
registerBeanPostProcessors BeanPostProcessorBeanFactoryPostProcessor 的区别、AOP 处理器注册
onRefresh 内嵌 Tomcat 启动原理、Spring Boot 为什么不需要外部部署
finishBeanFactoryInitialization Bean 生命周期、三级缓存解决循环依赖、AOP 动态代理生成时机
执行 Runner ApplicationRunnerCommandLineRunner 的使用场景与区别

总结

白话文通俗总结(最易记忆)

  1. 认清身份:先搞清楚自己是 Web 应用还是普通应用。
  2. 准备粮草:加载所有配置(yml、命令行、环境变量)。
  3. 建好空房:创建一个空的 IOC 容器。
  4. 绘制图纸:扫描所有 Bean 和自动配置(SPI),生成 BeanDefinition。
  5. 准备工具:注册所有 Bean 后置处理器(准备处理 Autowired、AOP、事务)。
  6. 启动引擎:启动内嵌 Tomcat。
  7. 生产产品:批量实例化所有单例 Bean,走生命周期、解决循环依赖、生成 AOP 代理。
  8. 收尾工作:执行启动后的回调 Runner。
  9. 开门迎客:发布启动成功事件,服务正式就绪。

面试高频“一句话总结”

“SpringBoot 启动就是:先准备 Environment 环境,再创建 IOC 容器;通过 SPI 机制加载自动配置类并扫描组件生成 BeanDefinition;接着注册 BeanPostProcessor;然后在 onRefresh 中启动内嵌 Tomcat;最后在 finishBeanFactoryInitialization 中批量实例化单例 Bean,期间通过三级缓存解决循环依赖并生成 AOP 代理,最终发布启动成功事件对外提供服务。”

掌握以上流程,不仅能让你在面试中对答如流,在遇到 BeanCreationExceptionCircularReference 或自动配置失效等启动报错时,也能迅速定位到是哪个阶段、哪个后置处理器出了问题。

更多推荐