面试知识库
进阶

SpringBoot启动流程#

一句话答案#

SpringApplication.run() 分两步:构造阶段推断应用类型、加载 Initializers/Listeners;run 阶段准备 Environment、创建并 refresh 容器(自动配置、Bean 创建都在 refresh 里)、启动内嵌容器、回调 Runner。

核心要点

两个阶段:new SpringApplication() 构造 + run() 执行

① 构造阶段(new SpringApplication(主类)):

  • 推断应用类型 WebApplicationType(NONE / SERVLET / REACTIVE,靠 classpath 判断)
  • spring.factories 加载 ApplicationContextInitializerApplicationListener
  • 推断主配置类(带 main 方法的类)

② run() 阶段:

1. 获取并启动 SpringApplicationRunListeners(发布 starting 事件)
2. 准备 Environment(加载 application.yml、系统变量、命令行参数)
   → 发布 environmentPrepared 事件
3. 打印 Banner
4. 创建 ApplicationContext(按应用类型选实现,如 AnnotationConfigServletWebServerApplicationContext)
5. prepareContext:把主配置类注册为 BeanDefinition、执行 Initializer 回调
6. refreshContext(context)  ← 核心,调用 AbstractApplicationContext.refresh()
7. afterRefresh、发布 started 事件
8. 调用 ApplicationRunner / CommandLineRunner
9. 发布 running 事件
plaintext

refresh() 里发生了什么(最常被深挖):

  • invokeBeanFactoryPostProcessors:执行 BFPP,ConfigurationClassPostProcessor 在此解析 @Configuration、@ComponentScan、@Import,自动配置(@EnableAutoConfiguration)也在这一步导入
  • registerBeanPostProcessors:注册 BPP(含 AOP 的 AbstractAutoProxyCreator)
  • finishBeanFactoryInitialization:实例化所有非懒加载单例 Bean(走 Bean生命周期
  • onRefresh:Web 容器在此创建并启动内嵌 Tomcat
面试回答(2分钟版)

SpringBoot 启动从 main 方法里的 SpringApplication.run() 进入,可以拆成构造和运行两个阶段。构造阶段会根据 classpath 推断这是 Servlet、Reactive 还是非 Web 应用,然后从 spring.factories 里加载所有的 ApplicationContextInitializer 和 ApplicationListener,并记录主配置类。run 方法是真正的启动流程:先启动一组 RunListener 发布 starting 事件,然后准备 Environment,把 application.yml、命令行参数、系统环境变量都加载进来,这时候会发布 environmentPrepared 事件,配置中心、日志系统初始化都监听这个时机。接着根据应用类型创建对应的 ApplicationContext,把主配置类注册成 BeanDefinition,然后调用最核心的 refreshContext,也就是 Spring 容器的 refresh 方法。refresh 里 ConfigurationClassPostProcessor 会解析配置类并触发 @EnableAutoConfiguration 的自动配置,注册各种 BeanPostProcessor,然后实例化所有非懒加载的单例 Bean,Web 应用还会在 onRefresh 阶段创建并启动内嵌的 Tomcat。容器刷新完成后发布 started 事件,回调我们实现的 ApplicationRunner 或 CommandLineRunner,最后发布 running 事件,整个应用就绪。简单说,自动配置和 Bean 创建并不是 SpringBoot 特有的魔法,它们都发生在标准的 Spring refresh 流程里,SpringBoot 只是在外面包了一层启动编排和事件机制。

追问与易错

追问方向:

  • “自动配置具体在启动的哪一步生效?”→ 在 refresh() 的 invokeBeanFactoryPostProcessors 阶段,由 ConfigurationClassPostProcessor 处理 @EnableAutoConfiguration,通过 AutoConfigurationImportSelector 导入候选配置类并经 @Conditional 过滤,详见 SpringBoot自动配置原理
  • “内嵌 Tomcat 是什么时候启动的?”→ 在 refresh() 的 onRefresh 阶段,由 ServletWebServerApplicationContext 创建 WebServer 并启动,不是 run 方法直接 new 出来的
  • “ApplicationRunner 和 CommandLineRunner 区别?”→ 都在容器刷新完成后、应用就绪前执行,可用于启动初始化;区别只是参数形式,CommandLineRunner 收到原始 String[] args,ApplicationRunner 收到封装好的 ApplicationArguments,多个 Runner 用 @Order 控制顺序
  • “启动过程中能插入自定义逻辑的扩展点有哪些?”→ ApplicationContextInitializer(容器刷新前)、各种 ApplicationListener 监听启动事件、BeanFactoryPostProcessor / BeanPostProcessor、Runner 回调
  • “SpringApplication 怎么知道是不是 Web 应用?”→ 看 classpath:有 DispatcherServlet 相关类是 SERVLET,有 WebFlux 相关类是 REACTIVE,都没有就是 NONE

易错点:

  • ❌ 认为自动配置是 SpringBoot 独立于 Spring 的机制——它就发生在 Spring 的 refresh 里
  • ❌ 把 Bean 实例化说成在 run 方法里——单例 Bean 是在 refresh 的 finishBeanFactoryInitialization 创建的
  • ❌ 混淆 environmentPrepared 和 contextRefreshed 事件的时机