SpringBoot 的启动流程是怎样的?
面试考察点
- 源码级理解深度:面试官不仅仅是想听你背流程,更想知道你是否真正跟过
SpringApplication.run()的源码,能不能说出关键类和方法的调用链路。 - 自动配置原理:Spring Boot 最核心的特性就是 "约定优于配置",启动流程中如何实现自动配置是必考点。
- 扩展点掌握:能否说出启动过程中有哪些扩展点(
ApplicationContextInitializer、ApplicationRunner等),以及在实际项目中如何利用这些扩展点。
核心答案
一切从 main() 方法里这行代码开始:
@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
SpringApplication.run() 的完整启动流程可以概括为 7 个核心步骤:
| 步骤 | 做了什么 | 关键源码方法 |
|---|---|---|
| 1 | 创建 SpringApplication 对象 | new SpringApplication() |
| 2 | 推断应用类型(Servlet / Reactive / None) | WebApplicationType.deduce() |
| 3 | 加载 ApplicationContextInitializer 和 ApplicationListener | getSpringFactoriesInstances() |
| 4 | 创建并准备 ApplicationContext | createApplicationContext() |
| 5 | 执行 BeanDefinition 扫描和加载 | refresh() |
| 6 | 自动配置(@EnableAutoConfiguration) | AutoConfigurationImportSelector |
| 7 | 执行 CommandLineRunner / ApplicationRunner | callRunners() |
深度解析
一、整体启动流程图
上图展示了 Spring Boot 从 run() 到应用完全启动的全过程。整体分为 5 个阶段:
- 阶段一(创建
SpringApplication):这是启动的准备工作。推断当前是普通 Java 应用还是 Web 应用(Servlet 或 Reactive),通过 SPI 机制加载所有的ApplicationContextInitializer和ApplicationListener。这一步还不会创建 IoC 容器。 - 阶段二(创建
ApplicationContext):根据阶段一推断出的应用类型,创建对应的 IoC 容器。Servlet 应用创建AnnotationConfigServletWebServerApplicationContext,Reactive 应用创建对应的 Reactive 容器。同时准备Environment(读取配置文件、环境变量等),打印 Banner。 - 阶段三(准备上下文):把
Environment绑定到容器上,执行所有ApplicationContextInitializer的initialize()方法,把主配置类注册为BeanDefinition。 - 阶段四(
refresh()刷新容器):这是整个启动流程中最核心也最复杂的一步。包括 Bean 定义的扫描与注册、自动配置的生效、所有单例 Bean 的创建和依赖注入、内嵌 Tomcat/Jetty 的启动。可以说 Spring 容器的 "心脏跳动" 就在这一步。 - 阶段五(执行 Runner):容器完全就绪后,执行所有
CommandLineRunner和ApplicationRunner,应用开始对外提供服务。
二、第一步:new SpringApplication() 做了什么?
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 1. 推断应用类型:Servlet、Reactive、还是非 Web
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 2. 从 spring.factories 加载 ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 3. 从 spring.factories 加载 ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(
ApplicationListener.class));
// 4. 推断主配置类(main 方法所在的类)
this.mainApplicationClass = deduceMainApplicationClass();
}
这里有个关键点:SPI 机制(Spring Factories)。Spring Boot 通过 SpringFactoriesLoader 扫描所有 jar 包 META-INF/spring.factories 文件,加载里面配置的类。这是自动配置的基础。
三、自动配置是怎么生效的?
@SpringBootApplication 是个复合注解,拆开来看:
@SpringBootApplication
├── @SpringBootConfiguration // 本质就是 @Configuration
├── @EnableAutoConfiguration // 核心!开启自动配置
│ └── @Import(AutoConfigurationImportSelector.class)
└── @ComponentScan // 组件扫描,扫描主类同包及子包下的组件
自动配置的关键在 @EnableAutoConfiguration,它导入的 AutoConfigurationImportSelector 做了这些事:
上图展示了自动配置的完整过滤链路。核心逻辑就是:
- 先从
spring.factories里读出一大堆xxxAutoConfiguration(Spring Boot 2.7+ 也支持META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件)。 - 然后通过
@ConditionalOnXxx注解逐个过滤,满足条件的才生效。比如DataSourceAutoConfiguration只有 classpath 上有数据库驱动时才生效。 - 用户自定义的 Bean 优先级更高。
@ConditionalOnMissingBean保证只有用户没配的时候,Spring Boot 才提供默认实现。
这就是 Spring Boot "约定优于配置" 的精髓——你不用配,我给你默认的;你要配,你的优先。
四、refresh() 里面做了什么?
refresh() 是 Spring 容器启动的核心方法,定义在 AbstractApplicationContext 中,包含 12 个步骤:
public void refresh() throws BeansException, IllegalStateException {
// 1. 准备刷新(设置启动时间、状态标记)
prepareRefresh();
// 2. 获取 BeanFactory(创建 DefaultListableBeanFactory)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. 准备 BeanFactory(设置类加载器、后置处理器等)
prepareBeanFactory(beanFactory);
// 4. 后置处理 BeanFactory(模板方法,子类扩展)
postProcessBeanFactory(beanFactory);
// 5. 执行 BeanFactoryPostProcessor(扫描 @Component、@Configuration 等)
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 注册 BeanPostProcessor(AOP、@Autowired 等都在这里)
registerBeanPostProcessors(beanFactory);
// 7. 初始化消息源(国际化)
initMessageSource();
// 8. 初始化事件广播器
initApplicationEventMulticaster();
// 9. 初始化其他特殊 Bean(模板方法,子类扩展)
onRefresh();
// ↑ Spring Boot 在这里启动内嵌 Tomcat!
// 10. 注册监听器
registerListeners();
// 11. 实例化所有非懒加载的单例 Bean
finishBeanFactoryInitialization(beanFactory);
// 12. 完成刷新(发布 ContextRefreshedEvent)
finishRefresh();
}
其中最关键的三步:
- 第 5 步:执行
BeanFactoryPostProcessor,这一步会扫描@Component、@Configuration等注解,把 Bean 定义注册到容器中。自动配置的AutoConfigurationImportSelector也是在这里被触发的。 - 第 11 步:实例化所有非懒加载的单例 Bean,完成依赖注入。你的
@Service、@Controller、@Repository标注的类,全在这一步被创建。 - 第 9 步(
onRefresh()):Spring Boot 重写了这个方法,在里头启动了内嵌的 Web 容器(Tomcat / Jetty / Undertow)。所以 Tomcat 是在refresh()过程中、所有 Bean 创建之前就启动了。
五、@SpringBootApplication 注解拆解
面试的时候如果被问到这个注解的含义,直接画出来就行:
// @SpringBootApplication 等价于以下三个注解的组合
@SpringBootConfiguration // 标记当前类是配置类(等同于 @Configuration)
@EnableAutoConfiguration // 开启自动配置(核心!)
@ComponentScan( // 组件扫描
excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)
}
)
public @interface SpringBootApplication { }
@ComponentScan 默认扫描主启动类所在包及其子包,这也是为什么你的 @Controller、@Service 必须放在启动类同包或子包下,否则扫描不到。
面试高频追问
-
追问一:Spring Boot 自动配置的原理是什么?
- 核心是
@EnableAutoConfiguration→AutoConfigurationImportSelector→ 从spring.factories(或AutoConfiguration.imports)加载所有自动配置类 → 通过@ConditionalOnXxx条件过滤 → 满足条件的自动注册 Bean。一句话:先全量加载,再按条件过滤。
- 核心是
-
追问二:
BeanFactory和ApplicationContext有什么区别?BeanFactory是 Spring 的顶层容器接口,只提供最基本的 Bean 管理功能(懒加载)。ApplicationContext是它的子接口,扩展了国际化、事件发布、AOP 集成、资源加载等功能。Spring Boot 启动时创建的就是ApplicationContext,而不是直接用BeanFactory。
-
追问三:如何自定义一个 Starter?
- 核心就是写一个
xxxAutoConfiguration类(用@ConditionalOnXxx做条件控制),然后在META-INF/spring.factories里注册。Spring Boot 启动时会自动加载。Spring Boot 2.7+ 推荐用META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件替代spring.factories。
- 核心就是写一个
常见面试变体
- "说一下 Spring Boot 的自动配置原理?"
- "
@SpringBootApplication注解由哪些注解组成?" - "Spring Boot 启动时内嵌 Tomcat 是什么时候启动的?"
- "
spring.factories文件的作用是什么?"
记忆口诀
创建推断 → 准备上下文 → 刷新容器 → 执行 Runner
拆开就是:创建 SpringApplication → 创建 ApplicationContext → prepareContext → refresh() → callRunners()
其中
refresh()是最核心的:扫 Bean 定义 → 注册后置处理器 → 启动 Tomcat → 创建单例 Bean → 发布事件
总结
Spring Boot 启动流程的核心就是 SpringApplication.run() 这一条线:创建 SpringApplication 对象(推断类型、加载扩展点)→ 创建并准备 ApplicationContext(环境准备、执行 Initializer)→ refresh() 刷新容器(扫描注册 Bean、自动配置、启动 Tomcat、依赖注入)→ 执行 Runner。面试时抓住 "创建、准备、刷新、执行" 这四个阶段展开就行,其中 refresh() 和自动配置是面试官最关注的两个点。
