Spring Boot 2.7 中资源销毁的先后顺序

发布于:2025-09-05 ⋅ 阅读:(19) ⋅ 点赞:(0)

详细解析 AbstractApplicationContext 类中的 doClose() 方法,并总结在 Spring Boot 2.7 中资源销毁的先后顺序。

方法详解

doClose() 方法是 Spring 应用上下文关闭过程的核心方法。它被设计为 protected,意味着它主要供 Spring 框架内部调用(例如,由 close() 方法调用),但子类也可以重写它(通常通过 onClose() 钩子方法)以添加自定义的关闭逻辑。

方法执行流程如下:

  1. 状态检查与原子性关闭确认

    if (this.active.get() && this.closed.compareAndSet(false, true)) {
    
    • this.active.get(): 检查上下文当前是否处于活动状态。只有在活动状态下才需要执行关闭操作。
    • this.closed.compareAndSet(false, true): 这是一个原子操作 (CAS),它将 closed 状态从 false 设置为 true。这个操作是线程安全的关键,它确保多个线程同时调用 close() 时,只有其中一个线程能成功进入关闭流程,后续的调用将直接返回,避免重复关闭。
  2. 日志记录

    if (logger.isDebugEnabled()) {
        logger.debug("Closing " + this);
    }
    
    • 如果启用了 Debug 日志,则记录一条上下文正在关闭的日志。
  3. LiveBeansView 注销 (非原生镜像环境)

    if (!NativeDetector.inNativeImage()) {
        LiveBeansView.unregisterApplicationContext(this);
    }
    
    • LiveBeansView 是 Spring 的一个工具类,用于提供当前应用中所有活动 Bean 的实时视图(常用于 IDE 工具集成)。
    • NativeDetector.inNativeImage() 用于判断应用是否运行在 GraalVM 原生镜像中。在原生镜像中,此功能不被支持,因此跳过注销。
  4. 发布 ContextClosedEvent 事件

    try {
        publishEvent(new ContextClosedEvent(this));
    }
    catch (Throwable ex) {
        logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
    }
    
    • 重要步骤:发布一个 ContextClosedEvent 事件。
    • 所有实现了 ApplicationListener<ContextClosedEvent> 接口的 Bean 都会收到这个通知。
    • 这为其他组件在容器正式销毁前执行一些清理操作提供了机会(例如,关闭线程池、断开网络连接、持久化未保存的数据等)。
    • 异常被捕获并记录为警告,不会中断关闭流程。
  5. 停止 Lifecycle 组件

    if (this.lifecycleProcessor != null) {
        try {
            this.lifecycleProcessor.onClose();
        }
        catch (Throwable ex) {
            logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
        }
    }
    
    • 重要步骤:获取 LifecycleProcessor 并调用其 onClose() 方法。
    • LifecycleProcessor 会通知所有实现了 Lifecycle 接口的 Bean(例如,Tomcat 嵌入式服务器、Netty 服务器等),让它们执行标准的停止流程。
    • 这一步是为了避免在后续单个 Bean 销毁过程中出现延迟,优先停止那些需要优雅关闭的组件(如 Web 服务器)。
    • 异常同样被捕获并记录,不会中断流程。
  6. 销毁所有单例 Bean

    destroyBeans();
    
    • 核心步骤:调用 DefaultListableBeanFactorydestroySingletons() 方法。
    • 这是资源销毁的主体部分。它会:
      • 遍历所有已创建的单例 Bean。
      • 如果 Bean 实现了 DisposableBean 接口,则调用其 destroy() 方法。
      • 如果 Bean 在定义时指定了自定义的销毁方法(如 @Bean(destroyMethod="...") 或 XML 中的 destroy-method),则调用该方法。
    • 常见的清理工作在此发生,如:数据库连接池的关闭 (DataSource)、Hibernate SessionFactory 的关闭、缓存管理器的关闭等。
  7. 关闭底层 BeanFactory

    closeBeanFactory();
    
    • 该方法通常将 this.beanFactory 设置为 null,表示这个 BeanFactory 已不可用。
    • 对于可刷新的应用上下文(如 GenericApplicationContext),它还会重置序列化 ID,为下一次 refresh() 做准备。
  8. 调用子类钩子方法

    onClose();
    
    • 这是一个空的模板方法 (protected void onClose() {}),专为子类设计。
    • 子类(如 ServletWebServerApplicationContext)可以重写此方法,在上下文完全关闭之前添加一些特定的清理逻辑。
  9. 重置应用监听器列表

    if (this.earlyApplicationListeners != null) {
        this.applicationListeners.clear();
        this.applicationListeners.addAll(this.earlyApplicationListeners);
    }
    
    • applicationListeners 列表重置回最初在 refresh() 之前的状态(即 earlyApplicationListeners)。
    • 这确保了如果上下文之后被再次刷新(refresh()),监听器列表处于一个干净、正确的初始状态。
  10. 状态置为未激活

    this.active.set(false);
    
    • 最后,将 active 状态标志设置为 false。此时,整个上下文的关闭流程正式完成。

总结:Spring Boot 2.7 资源销毁执行顺序

在 Spring Boot 2.7 应用中,当应用上下文关闭时(例如,由于 JVM 关闭或调用 SpringApplication.exit()),资源销毁的严格先后顺序如下:

  1. 发布关闭事件

    • 首先发布 ContextClosedEvent。允许应用程序监听器 (ApplicationListener) 最先感知到关闭事件并做出响应。
  2. 停止 Lifecycle 组件

    • 调用所有 Lifecycle Bean 的 stop() 方法。这是停止嵌入式 Web 服务器(如 Tomcat、Netty)的关键时刻。服务器停止接收新请求,并开始优雅关闭现有连接。
  3. 销毁单例 Beans

    • 按依赖关系反向销毁所有单例 Bean(依赖方先于被依赖方销毁)。
    • 执行每个 Bean 的销毁方法:
      • @PreDestroy 注解的方法。
      • DisposableBean 接口的 destroy() 方法。
      • @Bean(destroyMethod="...") 中指定的自定义方法(例如,DataSourceclose 方法通常在这里被调用以关闭连接池)。
  4. 框架级清理

    • 执行特定于上下文类型的最终清理(通过 onClose() 钩子)。
    • 重置内部状态(如监听器列表)。

核心要点:

  • 事件驱动:自定义清理逻辑应优先考虑实现 ApplicationListener<ContextClosedEvent>,以便最早执行。
  • 优雅关闭:Web 服务器等需要优雅关闭的组件通过在 Lifecycle 阶段停止。
  • 资源释放:数据库连接、线程池等资源的释放主要在 Bean 销毁阶段通过各自的 destroy 方法完成。
  • 顺序保障:这个顺序确保了依赖项(如数据库连接池)在其使用者(如 DAO Bean)之后才被销毁,并且像 Web 服务器这样的基础设施最早开始停止,最大限度地保证了关闭过程的平稳和无残留。

网站公告

今日签到

点亮在社区的每一天
去签到