Spring Boot 启动流程源码解析:从 main() 到 Web 服务就绪
一句
SpringApplication.run()背后,藏着整个 Spring 生态的启动引擎。
你是否曾:
- 在面试被问:"Spring Boot 启动过程做了哪些事?"
- 遇到启动慢、Bean 找不到、配置不生效等问题却无从下手?
- 想自定义启动行为(如动态加载配置、埋点监控),但不知从何切入?
答案,都在 SpringApplication.run() 的源码里。
今天,我们就逐行拆解 Spring Boot 3.x(兼容 2.x)的启动主流程 ,带你从 main() 方法一路走到内嵌 Tomcat 启动完成!
一、入口:main() 方法
typescript
@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
看似简单,实则调用了 SpringApplication 的静态方法:
typescript
// 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);
}
✅ 关键点 :先构造
SpringApplication实例,再调用其run()方法。
二、阶段 1:构造 SpringApplication 对象
scss
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 1. 推断应用类型(SERVLET / REACTIVE / NONE)
this.properties.setWebApplicationType(WebApplicationType.deduceFromClasspath());
// 2. 从 spring.factories 加载 BootstrapRegistryInitializer
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 3. 从 spring.factories 加载 ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 4. 从 spring.factories 加载 ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 5. 推断主配置类(即包含 main 方法的类)
this.mainApplicationClass = deduceMainApplicationClass();
}
🔑 核心动作:
-
推断 Web 类型:
cssSERVLET:classpath 中存在 Spring MVC 相关类(如 DispatcherServlet) REACTIVE:存在 WebFlux 相关类(如 DispatcherHandler) NONE:非 Web 应用(如批处理、定时任务) -
加载扩展点 :通过
SpringFactoriesLoader读取META-INF/spring.factories中的 SPI 实现。
💡 这就是 Spring Boot 自动装配和扩展机制的起点。
三、阶段 2:执行 run(args) ------ 启动主流程
这是最核心的方法,我们分步解析:
步骤 1:准备监听器
ini
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
getRunListeners()返回所有SpringApplicationRunListener实例(默认是EventPublishingRunListener)starting()发布ApplicationStartingEvent
→ 可用于早期日志初始化、APM 埋点
步骤 2:准备 Environment(环境)
ini
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
在 prepareEnvironment() 中:
- 创建
Environment(StandardServletEnvironment) - 调用
environmentPrepared()→ 发布ApplicationEnvironmentPreparedEvent - 此时
application.properties已加载!
🌟 实战价值:Nacos/Apollo 客户端在此阶段注入远程配置!
步骤 3:创建 ApplicationContext(应用上下文)
ini
context = createApplicationContext();
根据 webApplicationType 选择上下文类型:
SERVLET→AnnotationConfigServletWebServerApplicationContextREACTIVE→AnnotationConfigReactiveWebServerApplicationContext
该上下文继承自 GenericApplicationContext,并具备 内嵌 Web 容器支持。
步骤 4:准备上下文
scss
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
内部关键操作:
- 注册
bannerBean - 应用所有
ApplicationContextInitializer - 发布
ApplicationContextInitializedEvent
⚠️ 注意:此时 Bean 还未实例化,只是定义已加载。
步骤 5:刷新上下文(Refresh)------ 最重量级阶段!
scss
refreshContext(context);
最终调用 AbstractApplicationContext.refresh()(Spring Framework 的核心方法):
scss
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1. 准备刷新(记录启动时间、设置活跃状态)
prepareRefresh();
// 2. 获取 BeanFactory(通常是 DefaultListableBeanFactory)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. 配置 BeanFactory(设置类加载器、表达式解析器等)
prepareBeanFactory(beanFactory);
// 4. 执行 BeanFactoryPostProcessor(如 @ConfigurationProperties 绑定)
invokeBeanFactoryPostProcessors(beanFactory);
// 5. 注册 BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// 6. 初始化 MessageSource(国际化)
initMessageSource();
// 7. 初始化事件广播器
initApplicationEventMulticaster();
// 8. 【模板方法】子类可扩展(如 ServletWebServerApplicationContext 会在此启动内嵌容器)
onRefresh();
// 9. 注册监听器
registerListeners();
// 10. 实例化所有非懒加载的单例 Bean!
finishBeanFactoryInitialization(beanFactory);
// 11. 完成刷新(发布 ContextRefreshedEvent)
finishRefresh();
}
}
🔥 重点子阶段解析:
-
invokeBeanFactoryPostProcessors→
ConfigurationClassPostProcessor扫描@Component、@Bean,解析自动配置类(spring.factories中的EnableAutoConfiguration) -
onRefresh()(在 Servlet 上下文中)typescript@Override protected void onRefresh() { super.onRefresh(); try { createWebServer(); // 启动内嵌 Tomcat/Jetty } } -
finishBeanFactoryInitialization→ 调用
preInstantiateSingletons(),触发所有单例 Bean 的创建(包括依赖注入、@PostConstruct)
步骤 6:执行 Runner 启动完成
scss
/ 执行 CommandLineRunner / ApplicationRunner
callRunners(context, applicationArguments);
✅ 此时服务已完全就绪,可处理请求!
四、启动流程全景图(简化版)
scss
ain()
↓
new SpringApplication()
├── 推断 Web 类型
├── 加载 Initializers & Listeners
↓
run(args)
├── starting() → ApplicationStartingEvent
├── prepareEnvironment() → 加载 application.properties
├── createApplicationContext()
├── prepareContext() → 注册主配置类
├── refreshContext()
│ ├── invokeBeanFactoryPostProcessors → 自动配置生效
│ ├── onRefresh() → 启动内嵌 Web 容器
│ └── finishBeanFactoryInitialization → 初始化所有 Bean
├── callRunners() → 执行启动后任务
五、学源码有什么用?实战场景举例
| 场景 | 利用的启动阶段 | 扩展方式 |
|---|---|---|
| 动态加载远程配置 | environmentPrepared |
实现 EnvironmentPostProcessor |
| 启动耗时分析 | starting() / running() |
自定义 SpringApplicationRunListener |
| 服务注册延迟 | ContextRefreshedEvent 后 |
监听事件,确保 Bean 全部就绪 |
| 自定义 Banner | prepareContext 阶段 |
实现 Banner 接口 |
| 避免循环依赖报错 | 理解 finishBeanFactoryInitialization 顺序 |
调整依赖关系或使用 @Lazy |
📌 关注我 ,每天5分钟,带你从 Java 小白变身编程高手!
👉 点赞 + 关注,让更多小伙伴一起进步!