1. 概述
SpringBoot 的启动流程并非黑盒,其核心逻辑可以高度概括为两个主要阶段:初始化阶段(构建 SpringApplication 实例)与 运行阶段(执行 run 方法并刷新容器)。理解这一流程有助于排查启动错误、优化启动速度以及进行自定义扩展。
2. 阶段一:初始化 (Initialization)
在调用 SpringApplication.run() 之前或初期,系统会创建一个 SpringApplication 对象。此阶段主要完成“环境摸底”和“组件预备”,尚未创建 IoC 容器。
- 推断应用类型:检查 Classpath 中是否存在 Servlet 或 Reactive 相关类,判断应用是 Standard、Servlet Web 还是 Reactive Web 类型 。
- 加载核心组件:利用 SPI 机制(读取
META-INF/spring.factories或org.springframework.boot.autoconfigure.AutoConfiguration.imports),加载ApplicationContextInitializer和ApplicationListener。这些组件将在后续生命周期中发挥作用 。 - 确定主类:通过栈跟踪信息推断出包含
main方法的类,作为配置源的根节点。
3. 阶段二:运行 (Execution)
执行 application.run(args) 方法,这是启动的主战场,主要包含以下关键步骤:
3.1 前置准备
- 启动监听:获取
SpringApplicationRunListeners并广播“正在启动”事件,同时开启计时器 。 - 准备环境 (Prepare Environment):创建并配置
Environment对象。此时会加载系统属性、环境变量、命令行参数以及application.yml/properties配置文件 。 - 打印 Banner:在控制台输出 SpringBoot 的标志性图案(可自定义或关闭)。
3.2 容器构建与刷新 (核心)
- 创建上下文 (Create Context):根据第一阶段推断的应用类型,实例化具体的
ApplicationContext(如AnnotationConfigServletWebServerApplicationContext)。 - 准备上下文 (Prepare Context):将准备好的
Environment注入容器,应用之前加载的Initializer,并将主启动类注册为 BeanDefinition。 - 刷新上下文 (Refresh Context):这是最核心的步骤,委托给 Spring Framework 的标准
refresh()流程:- Bean 工厂预处理:准备 BeanFactory。
- 自动配置:解析
@EnableAutoConfiguration,根据条件注解(@Conditional)加载自动配置类,实例化所有非懒加载的单例 Bean 。 - 启动内嵌容器:如果是 Web 应用,在此阶段启动内嵌的 Tomcat、Jetty 或 Undertow 服务器,并监听端口。
3.3 收尾工作
- 刷新后处理:停止计时器,记录启动耗时日志。
- 执行 Runner:查找容器中所有实现了
CommandLineRunner或ApplicationRunner接口的 Bean,并按顺序执行其run方法。这为开发者提供了在应用完全启动后执行初始化逻辑的标准入口 。 - 完成启动:广播
ApplicationReadyEvent事件,返回ApplicationContext实例,应用正式对外提供服务。
4. 启动流程可视化
以下 Mermaid 流程图清晰展示了从初始化到运行结束的核心路径:
graph TD
Start([开始: SpringApplication.run]) --> Init[阶段一: 初始化 SpringApplication]
subgraph InitPhase [初始化阶段]
Init --> InferType[1. 推断应用类型<br/>Web/Reactive/None]
InferType --> LoadComponents[2. 加载初始化器与监听器<br/>SPI机制读取 spring.factories]
LoadComponents --> FindMain[3. 推断主启动类]
end
FindMain --> Run[阶段二: 执行 run 方法]
subgraph RunPhase [运行阶段]
Run --> ListenersStart[4. 启动监听器 & 开启计时]
ListenersStart --> PrepEnv[5. 准备环境 Environment<br/>加载配置文件/环境变量]
PrepEnv --> PrintBanner[6. 打印 Banner]
PrintBanner --> CreateContext[7. 创建 ApplicationContext<br/>IoC 容器实例化]
CreateContext --> PrepContext[8. 准备上下文<br/>注入环境/应用初始化器]
PrepContext --> Refresh[9. 刷新上下文 Refresh<br/>核心步骤]
subgraph RefreshCore [Refresh 内部关键动作]
Refresh --> AutoConfig[9.1 自动配置 & Bean 装配]
AutoConfig --> StartServer[9.2 启动内嵌 Web 服务器<br/>Tomcat/Jetty等]
end
StartServer --> AfterRefresh[10. 刷新后处理]
AfterRefresh --> RunRunners[11. 执行 CommandLineRunner/<br/>ApplicationRunner]
RunRunners --> Finish[12. 广播启动完成事件 & 返回上下文]
end
Finish --> End([结束: 应用就绪])
style InitPhase fill:#e1f5fe,stroke:#01579b,stroke-width:2px
style RunPhase fill:#fff3e0,stroke:#e65100,stroke-width:2px
style RefreshCore fill:#f3e5f5,stroke:#4a148c,stroke-width:2px,stroke-dasharray: 5 5
style Refresh fill:#ffccbc,stroke:#bf360c,stroke-width:2px
图解说明:
- 蓝色区域(初始化阶段):主要做准备工作,确定“是什么应用”以及“有哪些组件可用”,此时尚未创建 IoC 容器。
- 橙色区域(运行阶段):真正的启动过程,核心是构建和刷新 IoC 容器。
- 紫色虚线框(Refresh 核心):这是最耗时的部分,Spring 在此处完成所有 Bean 的创建、依赖注入以及内嵌服务器的启动。
- 流程走向:从上至下,线性执行,其中
Refresh Context是最关键的转折点,标志着 Spring 容器正式可用。
5. 关键总结
- SPI 机制是关键:SpringBoot 的扩展性(如自动配置、监听器加载)高度依赖于 SPI 机制读取配置文件。
- Refresh 是重心:绝大部分启动时间消耗在
refresh()阶段,特别是 Bean 的扫描、创建和内嵌服务器的启动。 - 扩展点清晰:
- 若需在环境准备前干预:使用
SpringApplicationRunListener。 - 若需在容器刷新前干预:使用
ApplicationContextInitializer。 - 若需在启动完成后执行业务逻辑:使用
CommandLineRunner/ApplicationRunner。
- 若需在环境准备前干预:使用
6. 参考流程图逻辑
[开始]
↓
[推断类型 / 加载SPI组件]
↓
[准备 Environment]
↓
[创建 Context]
↓
[Refresh: 自动配置 + 启动Tomcat]
↓
[执行 Runners]
↓
[结束]
注:本笔记基于 SpringBoot 通用启动机制整理,具体细节可能因版本(2.x vs 3.x)略有差异,但核心脉络保持一致。
评论区