说来惭愧,学习java已有三四年之久,工作一年有余,今天突然想到,SpringBoot项目是如何启动的,一时间竟然哑口无言,只能咂摸咂摸反思一下,把原理记录下来。
接下来我将系统地记录 Spring Boot 应用程序从执行 main 方法到完全启动并对外提供服务的全过程。将详细剖析 SpringApplication.run() 的核心逻辑、自动配置机制的实现原理、内嵌 Web 服务器(以 Tomcat 为例)的生命周期管理。通过对关键类、接口和事件的分析,从而揭示 Spring Boot “约定优于配置”理念背后复杂而精密的实现细节。
从一行代码开始的旅程
Spring Boot 极大地简化了 Spring 应用的开发与部署,其最直观的体现便是开发者只需通过一个带有 @SpringBootApplication 注解的主类和一行 SpringApplication.run(MyApplication.class, args); 代码,即可启动一个功能完备的应用程序 。这简洁的表象之下,隐藏着一个精心设计的、高度可扩展的自动化启动流程。
一、 启动的起点:main 方法与 SpringApplication 实例
一个标准的 Spring Boot 应用的生命周期始于其主类的 main 方法。此方法的核心职责是调用 SpringApplication.run() 静态方法,从而委托 Spring Boot 框架接管后续的所有启动工作 。SpringApplication.run() 方法的执行可以分为两个主要阶段:SpringApplication 实例的初始化和 run 实例方法的执行。
1.1 SpringApplication 实例的创建与配置
在 run 方法内部,首先会创建一个 SpringApplication 的实例 。在实例化过程中,SpringApplication 会执行一系列重要的初始化操作:
- 推断应用类型: 它会检查类路径,判断当前是标准的、响应式的还是无 Web 环境的应用(例如,通过是否存在 javax.servlet.Servlet 或 org.springframework.web.reactive.DispatcherHandler 来决定 Web 应用类型为 SERVLET 或 REACTIVE)。
- 加载初始化器与监听器: 这是 Spring Boot 扩展机制的核心。它使用 SpringFactoriesLoader 工具类,从所有依赖 JAR 包的 META-INF/spring.factories 文件中,查找并加载 ApplicationContextInitializer 和 ApplicationListener 接口的所有实现类 。这些加载到的类实例会被收集起来,用于在启动过程的特定阶段执行回调 。
- 确定主应用类: 它会分析调用栈,推断出包含 main 方法的主配置类。
二、 核心驱动:run 方法的详细执行步骤
SpringApplication 实例创建并初步配置完成后,其 run() 实例方法被调用,这标志着应用启动流程的正式开始。这个过程被精心划分为多个阶段,并通过事件监听机制对外广播,从而具备了极高的可观察性和扩展性。
2.1 第一阶段:启动监听与环境准备
-
获取并启动 SpringApplicationRunListeners:run 方法首先会再次访问 META-INF/spring.factories,加载所有 SpringApplicationRunListener 的实现 。这些监听器被封装在一个名为 SpringApplicationRunListeners 的复合对象中 。随后,立即调用所有监听器的 starting() 方法,并广播 ApplicationStartingEvent 事件 。这是整个启动流程中最早的回调点。
-
创建并准备 Environment:接下来,应用的环境(Environment)被创建。此环境对象将用于整合所有配置来源,包括操作系统的环境变量、JVM 的系统属性、命令行参数,以及后续将要加载的配置文件(如 application.properties 或 application.yml)。当环境准备就绪后,SpringApplicationRunListeners 会调用 environmentPrepared() 方法,并广播 ApplicationEnvironmentPreparedEvent 事件。
2.2 第二阶段:创建与配置 ApplicationContext
-
创建 ApplicationContext:根据之前推断的应用类型,Spring Boot 会选择并实例化一个合适的 ApplicationContext 实现。对于传统的 Web 应用,通常会创建 AnnotationConfigServletWebServerApplicationContext 。
-
预处理 ApplicationContext:在 ApplicationContext 正式“刷新”(refresh)之前,Spring Boot 提供了一个重要的扩展点。
- 执行 ApplicationContextInitializer:此时,之前加载的所有 ApplicationContextInitializer 的 initialize() 方法会被依次调用 。这些初始化器可以在 Spring 容器加载 Bean 定义之前,对上下文进行编程式的配置,例如动态注册 Bean 或修改环境属性。
- 发布 contextPrepared 与 contextLoaded 事件:在 ApplicationContext 创建后,SpringApplicationRunListeners 会调用 contextPrepared() 方法 。在所有 ApplicationContextInitializer 执行完毕且 Bean 定义已加载到上下文中(但尚未创建 Bean 实例)之后,会调用 contextLoaded() 方法,并广播 ApplicationPreparedEvent 事件。
2.3 第三阶段:刷新 ApplicationContext
ApplicationContext 的 refresh() 方法是整个 Spring 框架的核心,也是 Spring Boot 启动流程中最关键、最复杂的一步。调用此方法会触发 Spring 容器的完整初始化生命周期 。主要工作包括:
- 调用 BeanFactoryPostProcessor:执行所有已注册的 BeanFactoryPostProcessor,它们可以读取和修改 Bean 定义的元数据 。自动配置的核心逻辑就发生在此阶段。
- 注册 BeanPostProcessor:注册所有 BeanPostProcessor,它们可以在 Bean 实例化和初始化的前后进行干预 。
- 实例化单例 Bean:容器会创建所有非懒加载的单例 Bean,处理依赖注入(DI),并执行相关的初始化回调(如 PostConstruct 注解的方法)。
- 启动内嵌 Web 服务器:对于 Web 应用,refresh() 过程中的 onRefresh() 步骤会触发内嵌服务器的创建和启动。
三、 自动配置的魔法:@EnableAutoConfiguration 的工作原理
Spring Boot 的自动配置是其“约定优于配置”理念的基石。这一机制的核心由@SpringBootApplication 注解中的 @EnableAutoConfiguration 触发。
-
导入 AutoConfigurationImportSelector:@EnableAutoConfiguration 注解通过 @Import 导入了 AutoConfigurationImportSelector 类 。这个类是自动配置流程的总指挥。
-
加载自动配置类候选列表:AutoConfigurationImportSelector 的 selectImports 方法会利用 SpringFactoriesLoader 从类路径下所有 META-INF/spring.factories 文件中,读取 org.springframework.boot.autoconfigure.EnableAutoConfiguration 键下声明的所有自动配置类名 。
-
条件化评估与筛选:获取到候选列表后,并不会全部加载,而是进入一个精密的条件评估(Conditional Evaluation)阶段。
- @Conditional 注解族:每个自动配置类通常都会被一个或多个 @Conditional 派生注解修饰,如 @ConditionalOnClass(当类路径下存在某个类时生效)、@ConditionalOnBean(当容器中存在某个 Bean 时生效)、@ConditionalOnProperty(当某个配置属性满足特定值时生效)等 。
- Condition 接口:这些条件注解的背后是 Condition 接口,其 matches() 方法返回一个布尔值,决定了该条件是否满足 。ConditionEvaluator 类负责解析和执行这些条件判断 。
- 过滤配置类:AutoConfigurationImportSelector 会遍历所有候选配置类,排除那些条件不满足的类 。
-
注册 Bean 定义:最终通过筛选的自动配置类(它们本身是 @Configuration 类)会被 Spring 容器加载。这些类中定义的 @Bean 方法(同样可能带有 @Conditional 注解)会根据条件向容器中注册所需的 Bean,从而完成例如数据源、Web 服务器、消息队列等组件的自动化配置 。开发者可以通过设置 debug: true 来查看详细的 ConditionEvaluationReport,了解哪些自动配置被启用或禁用了。
四、 内嵌 Web 服务器的生命周期(以 Tomcat 为例)
对于 Web 应用,启动流程中一个至关重要的环节是内嵌 Servlet 容器的初始化。
-
自动配置与工厂创建:当 Spring Boot 检测到类路径上存在 spring-boot-starter-tomcat 依赖时,ServletWebServerFactoryAutoConfiguration 会被激活。这个配置类会负责创建一个 TomcatServletWebServerFactory 的 Bean 实例。这个工厂类是创建和配置 Tomcat 服务器的入口。
-
服务器实例化与配置:在 ApplicationContext 的 refresh 过程的 onRefresh 阶段,容器会找到 TomcatServletWebServerFactory 这个 Bean,并调用其 getWebServer() 方法。此方法内部执行以下操作:
- 创建 Tomcat 实例:实例化一个 org.apache.catalina.startup.Tomcat 对象。
- 基础配置:设置 Tomcat 的基础目录、端口号等。
- 创建 Connector:创建一个 Connector 组件,它负责监听指定端口(如 8080)并接收网络请求。可以通过 TomcatServletWebServerFactory 对 Connector 进行深度定制。
- 准备上下文 Context:配置 Tomcat 的 Engine、Host 和 Context 等核心容器组件。
- 创建 TomcatWebServer:将配置好的 Tomcat 实例封装成一个 TomcatWebServer 对象,该对象统一了对 Web 服务器的生命周期管理接口。
-
服务器启动与事件发布:TomcatWebServer 的构造函数或 initialize 方法会调用 this.tomcat.start(),从而启动整个 Tomcat 服务器及其所有组件(如 Connector 开始监听端口)
。服务器成功启动后,Spring Boot 会发布一个 WebServerInitializedEvent 事件,通知应用程序 Web 服务器已准备就绪。
五、 启动流程的收尾工作
当 ApplicationContext.refresh() 方法执行完毕后,启动流程进入最后阶段:
-
调用 Runner:Spring Boot 会从容器中查找所有实现了 ApplicationRunner 和 CommandLineRunner 接口的 Bean,并按顺序调用它们的 run 方法 。这为开发者提供了一个在应用启动完成后执行自定义业务逻辑的入口。
-
发布最终事件:
- 在调用 Runner 之前,SpringApplicationRunListeners 会调用 started() 方法,并广播 ApplicationStartedEvent。
- 在 Runner 调用完毕后,run 方法即将结束前,SpringApplicationRunListeners 会调用 running() 方法,并广播 ApplicationReadyEvent,标志着应用程序已完全准备好处理请求。
- 如果启动过程中出现任何异常,则会调用 finished()(在新版本中为 failed())方法,并广播 ApplicationFailedEvent。
至此,Spring Boot 应用的整个启动流程宣告完成。🐒🐒🐒😁

被折叠的 条评论
为什么被折叠?



