Spring Boot 应用的生命周期可以清晰地划分为三个主要阶段:
- 启动阶段 (Startup):这是最复杂也最重要的阶段。通过 SpringApplication.run() 方法启动,这个过程包括环境准备、创建并刷新 ApplicationContext(应用上下文)、实例化并配置所有 Beans、以及启动内嵌的 Web 服务器。
- 运行阶段 (Running):应用成功启动后进入此阶段。此时,内嵌的 Web 服务器(如 Tomcat)正在监听端口,接收并处理外部请求。同时,后台任务(如 @Scheduled 定时任务)也在此阶段执行。
- 关闭阶段 (Shutdown):当应用接收到关闭信号(如 Ctrl+C 或通过 Actuator 端点),它会执行一个优雅的关闭流程。这包括停止接收新请求、等待现有请求处理完成、销毁所有 Beans 并释放资源。
生命周期的可视化流程
为了更直观地理解,我们可以将整个生命周期看作一个流程图:
[用户执行 main 方法]
|
V
[1. 启动阶段 (SpringApplication.run)]
|
+--> 1a. 创建 SpringApplication 实例
|
+--> 1b. 准备环境 (Environment), 加载配置 (application.properties)
|
+--> 1c. 发布 ApplicationStartingEvent 事件
|
+--> 1d. 创建应用上下文 (ApplicationContext)
|
+--> 1e. 准备上下文 (Prepare Context), 执行 Initializers
|
+--> 1f. **刷新上下文 (Refresh Context)** <-- 这是Spring的核心
| |
| +--> 创建 BeanFactory, 加载 Bean 定义
| |
| +--> 执行 BeanFactoryPostProcessors
| |
| +--> **实例化所有单例 Bean (Bean 的生命周期在此发生)**
| |
| +--> **启动内嵌 Web 服务器 (如 Tomcat)**
| |
| +--> 发布 ContextRefreshedEvent 事件
|
+--> 1g. 执行 ApplicationRunner 和 CommandLineRunner
|
+--> 1h. 发布 ApplicationReadyEvent 事件 (标志着应用准备就绪)
|
V
[2. 运行阶段 (Application is Running)]
|
+--> Tomcat 监听端口, DispatcherServlet 处理请求
|
+--> @Scheduled 任务按时执行
|
+--> ... (应用持续提供服务) ...
|
V
[接收到关闭信号 (e.g., Ctrl+C)]
|
V
[3. 关闭阶段 (Graceful Shutdown)]
|
+--> 发布 ContextClosedEvent 事件
|
+--> **执行 Bean 的销毁回调 (@PreDestroy, DisposableBean)**
|
+--> 关闭应用上下文
|
+--> JVM 退出
第一阶段:启动阶段 (Startup) 详解
一切都始于 public static void main 方法中的 SpringApplication.run(MyApplication.class, args);。
1. 创建SpringApplication实例
run 方法首先会创建一个 SpringApplication 类的实例。在这个过程中,它会做几件事:
- 推断应用类型(是普通的、Web Servlet 还是 Web Reactive 应用)。
- 从 META-INF/spring.factories (旧版) 和 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (Spring Boot 3.x 推荐) 文件中加载 ApplicationContextInitializer 和 ApplicationListener 的实现类。
2. 准备环境和上下文
- 发布 ApplicationStartingEvent 事件:广播一个“应用开始启动了”的信号。
- 创建 Environment: 创建一个环境对象,用于存储配置信息。它会加载 application.properties 或 application.yml 文件中的配置,以及系统属性、环境变量等。
- 创建 ApplicationContext: 根据应用类型(Web 或非 Web)创建对应的应用上下文实例,例如 AnnotationConfigServletWebServerApplicationContext。
- 准备 ApplicationContext: 调用之前加载的 ApplicationContextInitializer 来对上下文进行一些初始化配置。
- 发布 ApplicationPreparedEvent 事件:广播一个“上下文准备好了,但 Bean 还没加载”的信号。
3. 刷新上下文 (context.refresh())
这是整个启动过程中最核心、最复杂的一步,它标志着 Spring 容器的正式启动。
- 创建 BeanFactory:创建用于管理 Bean 的工厂。
- 执行 BeanFactoryPostProcessor: 允许在所有 Bean 定义加载后,但在 Bean 实例化之前,对 Bean 的定义(元数据)进行修改。
- 实例化单例 Bean:IoC 容器会遍历所有 Bean 的定义,并创建它们的实例。这个过程本身也包含一个微型的Bean 生命周期:
- 实例化: 通过构造函数创建 Bean 实例。
- 属性填充: 依赖注入(@Autowired 等)发生在此处。
- Aware 接口回调: 如果 Bean 实现了 BeanNameAware, BeanFactoryAware 等接口,会调用相应的方法。
- @PostConstruct: 执行被此注解标记的初始化方法。
- InitializingBean: 调用 afterPropertiesSet() 方法。
- Bean 准备就绪。
- 启动内嵌 Web 服务器: 在刷新过程中,ServletWebServerFactory 这个 Bean 会被创建和初始化。它负责启动内嵌的 Tomcat(或其他服务器),并开始监听端口。
- 发布 ContextRefreshedEvent 事件:广播一个“整个上下文都刷新完毕,所有 Bean 都已加载并配置好”的信号。
4. 启动后处理
- 调用 Runner: 在上下文刷新之后,Spring Boot 会查找所有 ApplicationRunner 和 CommandLineRunner 类型的 Bean,并按顺序执行它们的 run 方法。这提供了一个在应用启动完成后执行自定义逻辑的入口点。
- 发布 ApplicationReadyEvent 事件:广播最后一个启动事件,标志着应用已完全准备好,可以对外提供服务了。
第二阶段:运行阶段 (Running)
- 此时,主线程通常已经结束,但由于 Tomcat 的服务器线程是非守护线程,所以 JVM 进程会继续运行。
- DispatcherServlet(由 WebMvcAutoConfiguration 配置)作为前端控制器,接收所有进入的 HTTP 请求,并将其分发给对应的 @Controller 中的处理方法。
- 如果应用中定义了 @Scheduled 注解的定时任务,它们会由一个后台线程池根据设定的 cron 表达式或速率准时触发执行。
第三阶段:关闭阶段 (Shutdown)
Spring Boot 应用支持优雅关闭 (Graceful Shutdown),这是一个非常重要的生产特性。
1. 触发关闭
关闭可以通过以下方式触发:
- 在命令行中按下 Ctrl+C (发送 SIGINT 信号)。
- 通过 Spring Boot Actuator 的 /shutdown 端点(需要启用和授权)。
- 调用 SpringApplication.exit() 方法。
2. 执行关闭流程
一旦触发,Spring Boot 会:
- 停止接收新请求:Web 服务器(如 Tomcat)会关闭其连接器(Connector),不再接受新的网络连接。
- 等待现有请求完成:它会等待一个超时时间(可通过 spring.lifecycle.timeout-per-shutdown-phase 配置),让正在处理中的请求能够执行完毕。
- 关闭应用上下文 (context.close()):
- 发布 ContextClosedEvent 事件。
- 调用所有 Bean 的销毁方法。这也有一个明确的顺序:
- 执行被 @PreDestroy 注解标记的方法。
- 如果 Bean 实现了 DisposableBean 接口,调用其 destroy() 方法。
- 清空资源并退出:所有 Bean 被销毁,资源被释放后,JVM 进程最终退出。
如何利用生命周期扩展点?
理解生命周期后,你就可以在合适的时机通过 Spring Boot 提供的扩展点来执行自定义逻辑:
- ApplicationContextInitializer: 在上下文刷新前,以编程方式配置上下文。
- ApplicationListener: 监听生命周期中的各种事件(如 ApplicationReadyEvent)来执行特定操作。
- @PostConstruct: 在 Bean 的所有依赖都注入后,执行初始化逻辑。
- ApplicationRunner/CommandLineRunner: 在整个应用启动完成后,执行一次性任务(如数据预加载、启动脚本等)。
- @PreDestroy/DisposableBean: 在应用关闭时,优雅地释放资源(如关闭数据库连接池、清理临时文件等)。
通过掌握这些阶段和扩展点,你就能更好地控制和理解你的 Spring Boot 3.x 应用的行为。