每日Java面试场景题知识点之-SpringBoot启动流程

每日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.Servletorg.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() 方法。该方法包含以下关键子步骤:

  1. prepareRefresh:刷新前准备,记录启动时间,设置活跃标志
  2. obtainFreshBeanFactory:创建/刷新BeanFactory,加载BeanDefinition
  3. prepareBeanFactory:对BeanFactory进行功能填充(类加载器、后置处理器等)
  4. postProcessBeanFactory:子类可重写的后置处理钩子
  5. invokeBeanFactoryPostProcessors :执行BeanFactoryPostProcessor,这是自动配置的核心入口,@EnableAutoConfiguration在此生效
  6. registerBeanPostProcessors:注册BeanPostProcessor
  7. initMessageSource:初始化国际化资源
  8. initApplicationEventMulticaster:初始化事件广播器
  9. onRefresh:子类特殊初始化,如内嵌Tomcat在此启动
  10. registerListeners:注册监听器
  11. finishBeanFactoryInitialization:实例化所有非懒加载的单例Bean
  12. 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);

调用所有实现了 ApplicationRunnerCommandLineRunner 的Bean,这是业务侧常用的启动后执行逻辑的扩展点。

4.14 发布ready事件

java 复制代码
listeners.ready(context, timeTakenToStartup);

触发 ready 事件,标志着SpringBoot应用正式启动完成,可以对外提供服务。

五、启动流程核心事件时间线

整个启动流程中,关键事件按时间顺序依次触发:

  1. starting → 应用启动开始
  2. environmentPrepared → 环境准备完成
  3. contextPrepared → 上下文创建完成
  4. contextLoaded → BeanDefinition加载完成
  5. started → 上下文刷新完成,Bean已就绪
  6. 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是如何启动的

refreshContextonRefresh 阶段,ServletWebServerApplicationContext 会调用 createWebServer(),该方法会从BeanFactory中获取 ServletWebServerFactory(默认TomcatServletWebServerFactory),创建并启动Web服务器。

七、总结

SpringBoot的启动流程可以用一句话概括:构造SpringApplication推断应用类型和加载扩展点,然后通过run方法完成环境准备、上下文创建、自动配置、Bean实例化和内嵌容器启动,最终发布ready事件表示应用就绪。

掌握这些核心流程,不仅能帮你应对面试,更能在实际开发中快速定位启动阶段的各类问题,是Java后端工程师的必备技能。

感谢读者观看

相关推荐
plainGeekDev2 小时前
null 判断 → Kotlin 可空类型
android·java·kotlin
糖拌西瓜皮2 小时前
Java开发者视角:深入理解Node.js异步编程模型
java·后端·node.js
plainGeekDev2 小时前
getter/setter → Kotlin 属性
android·java·kotlin
一线大码2 小时前
Smart-Doc 的简单使用
java·后端·restful
假如让我当三天老蒯3 小时前
回归基本功!前端的解构赋值、扩展运算符、剩余参数
前端·面试
Lee川3 小时前
Memory 模块深度解析(面试向)
人工智能·面试
MacroZheng4 小时前
Claude Code官方桌面端正式发布,夯爆了!
java·人工智能·后端
虚无境4 小时前
如何编写一个SpringBoot项目告警推送的Starter
java·prometheus·webhook
NE_STOP19 小时前
Vide Coding--AI编程工具的选择
java