每日Java面试场景题知识点之-SpringBoot启动流程
一、面试场景引入
面试官:"你平时都用SpringBoot开发,那你说说SpringBoot的启动流程是怎样的?"
这道题是Java后端面试中的高频题,考察的不仅是对框架的使用熟练度,更是对底层原理的理解深度。本文将从源码级别带你完整走一遍SpringBoot的启动流程。
二、一切始于run方法
当我们启动一个SpringBoot应用时,入口永远是这样的:
java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
这行看似简单的 SpringApplication.run(),实际上包含了SpringBoot启动的完整生命周期。我们可以将整个启动流程分为两大阶段:构造阶段 和运行阶段。
三、构造阶段:SpringApplication的初始化
当我们调用 new SpringApplication(primarySources) 时,框架主要完成了以下几件关键事情:
3.1 推断Web应用类型
通过检查类路径下是否存在特定的类,来决定应用的类型:
- 如果存在
javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext,则推断为 SERVLET 类型 - 如果存在
org.springframework.web.reactive.DispatcherHandler,则推断为 REACTIVE 类型 - 否则推断为 NONE(非Web应用)
源码对应:
java
this.webApplicationType = WebApplicationType.deduceFromClasspath();
3.2 加载ApplicationContextInitializer
通过SPI机制从 META-INF/spring.factories 中加载所有配置好的初始化器。这些初始化器会在容器刷新之前被回调执行,用于对ApplicationContext进行定制化设置。
java
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
3.3 加载ApplicationListener
同样通过SPI机制加载所有监听器,用于在启动过程中监听各种事件,如应用启动失败、上下文刷新完成等。
java
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
3.4 推断主应用类
通过异常栈帧信息推断出包含main方法的主应用类:
java
this.mainApplicationClass = deduceMainApplicationClass();
四、运行阶段:run方法核心流程
run() 方法是SpringBoot启动的真正核心逻辑,下面逐步拆解:
4.1 创建并启动计时器
java
StopWatch stopWatch = new StopWatch();
stopWatch.start();
用于记录应用启动耗时,启动完成后会输出日志。
4.2 创建引导上下文
java
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
创建引导上下文,用于在启动阶段提供环境共享和临时Bean的管理。
4.3 配置Headless属性
java
configureHeadlessProperty();
设置 java.awt.headless=true,确保在无显示设备的服务器环境下,图形相关操作不会抛出异常。
4.4 获取RunListeners并发送starting事件
java
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
加载所有的 SpringApplicationRunListener,并触发 starting 事件,标志着应用启动流程正式开始。
4.5 封装命令行参数
java
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
将main方法传入的命令行参数封装为 ApplicationArguments 对象,方便后续统一处理。
4.6 准备环境(关键步骤)
java
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
这是启动流程中极为重要的一步:
- 创建
ConfigurableEnvironment对象 - 加载配置文件(application.yml/properties)
- 解析命令行参数、系统属性、操作系统环境变量等多层配置源
- 按照严格的优先级顺序进行配置覆盖
- 触发
environmentPrepared事件
4.7 打印Banner
java
Banner printedBanner = printBanner(environment);
在控制台打印SpringBoot标志性的Banner图案,也可以自定义Banner。
4.8 创建ApplicationContext
java
context = createApplicationContext();
根据之前推断的Web应用类型,创建不同的ApplicationContext实现类:
- SERVLET类型 →
AnnotationConfigServletWebServerApplicationContext - REACTIVE类型 →
AnnotationConfigReactiveWebServerApplicationContext - NONE类型 →
AnnotationConfigApplicationContext
4.9 准备上下文
java
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
这一步完成了多项关键工作:
- 将Environment绑定到ApplicationContext
- 执行所有ApplicationContextInitializer的initialize方法
- 触发
contextPrepared事件 - 注册主配置类作为BeanDefinition
- 触发
contextLoaded事件
4.10 刷新上下文(最核心步骤)
java
refreshContext(context);
这是整个启动流程中最复杂、最核心的一步,底层调用的是Spring的 AbstractApplicationContext.refresh() 方法。该方法包含以下关键子步骤:
- prepareRefresh:刷新前准备,记录启动时间,设置活跃标志
- obtainFreshBeanFactory:创建/刷新BeanFactory,加载BeanDefinition
- prepareBeanFactory:对BeanFactory进行功能填充(类加载器、后置处理器等)
- postProcessBeanFactory:子类可重写的后置处理钩子
- invokeBeanFactoryPostProcessors :执行BeanFactoryPostProcessor,这是自动配置的核心入口,@EnableAutoConfiguration在此生效
- registerBeanPostProcessors:注册BeanPostProcessor
- initMessageSource:初始化国际化资源
- initApplicationEventMulticaster:初始化事件广播器
- onRefresh:子类特殊初始化,如内嵌Tomcat在此启动
- registerListeners:注册监听器
- finishBeanFactoryInitialization:实例化所有非懒加载的单例Bean
- finishRefresh:完成刷新,发布ContextRefreshedEvent
其中第9步 onRefresh,在Servlet Web应用中,会调用 createWebServer() 方法,完成内嵌Tomcat/Jetty/Undertow的启动。
4.11 执行afterRefresh
java
afterRefresh(context, applicationArguments);
刷新后的钩子方法,默认为空实现,供子类扩展使用。
4.12 停止计时器并发送started事件
java
stopWatch.stop();
listeners.started(context, timeTakenToStartup);
记录启动耗时,触发 started 事件,表示ApplicationContext已经刷新完成,Bean已就绪。
4.13 执行Runner
java
callRunners(context, applicationArguments);
调用所有实现了 ApplicationRunner 和 CommandLineRunner 的Bean,这是业务侧常用的启动后执行逻辑的扩展点。
4.14 发布ready事件
java
listeners.ready(context, timeTakenToStartup);
触发 ready 事件,标志着SpringBoot应用正式启动完成,可以对外提供服务。
五、启动流程核心事件时间线
整个启动流程中,关键事件按时间顺序依次触发:
- starting → 应用启动开始
- environmentPrepared → 环境准备完成
- contextPrepared → 上下文创建完成
- contextLoaded → BeanDefinition加载完成
- started → 上下文刷新完成,Bean已就绪
- ready → 应用完全启动,可接收请求
六、面试加分点
6.1 SpringBoot和Spring的启动区别
Spring的传统启动需要手动配置web.xml、DispatcherServlet、ContextLoaderListener等,而SpringBoot通过自动配置和内嵌Web容器,将这一切自动化,开发者只需一个main方法即可启动。
6.2 关键扩展点总结
- ApplicationContextInitializer:上下文刷新前的定制化
- ApplicationListener:监听启动过程中的各类事件
- SpringApplicationRunListener:监听run方法的各阶段
- BeanFactoryPostProcessor:Bean定义加载后、实例化前的修改
- BeanPostProcessor:Bean实例化后的前后置处理
- ApplicationRunner/CommandLineRunner:启动完成后的业务逻辑
6.3 内嵌Tomcat是如何启动的
在 refreshContext → onRefresh 阶段,ServletWebServerApplicationContext 会调用 createWebServer(),该方法会从BeanFactory中获取 ServletWebServerFactory(默认TomcatServletWebServerFactory),创建并启动Web服务器。
七、总结
SpringBoot的启动流程可以用一句话概括:构造SpringApplication推断应用类型和加载扩展点,然后通过run方法完成环境准备、上下文创建、自动配置、Bean实例化和内嵌容器启动,最终发布ready事件表示应用就绪。
掌握这些核心流程,不仅能帮你应对面试,更能在实际开发中快速定位启动阶段的各类问题,是Java后端工程师的必备技能。
感谢读者观看