news 2026/10/3 13:45:40

Spring DisposableBean 接口深度解析:Bean 销毁回调机制与容器关闭生命周期实战(spring-reading)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring DisposableBean 接口深度解析:Bean 销毁回调机制与容器关闭生命周期实战(spring-reading)
  • 示例工程
  • 文档

【免费下载链接】spring-reading

涵盖了 Spring 框架的核心概念和关键功能,包括控制反转(IOC)容器的使用,面向切面编程(AOP)的原理与实践,事务管理的方式与实现,Spring MVC 的流程与控制器工作机制,以及 Spring 中数据访问、安全、Boot 自动配置等方面的深入研究。此外,它还包含了 Spring 事件机制的应用、高级主题如缓存抽象和响应式编程,以及对 Spring 源码的编程风格与设计模式的深入探讨。

项目地址:https://gitcode.com/GitHub_Trending/sp/spring-reading
点击查看免费下载

DisposableBean是 Spring 框架自 12.08.2003 引入的核心生命周期接口,用于在 Bean 被容器销毁时执行资源清理,如关闭文件句柄、网络连接、数据库连接等。本文以 spring-reading 仓库中spring-interface-disposableBean模块为实战载体,完整讲解接口定义、最佳实践、容器关闭时的完整销毁调用链,以及原型 Bean、销毁方法优先级等注意事项,帮助读者彻底掌握 Spring 容器优雅关闭与资源释放的底层机制。

一、接口基本信息与源码

1.1 接口描述

DisposableBean接口允许 Bean 在销毁前执行某些资源清理操作。我们可以使用这个接口确保文件句柄、网络连接、数据库连接等资源被正确地释放或清理,避免资源泄漏。

从接口源码注释可以看出,该接口承担以下职责:

  • 当单独销毁作用域内的 Bean 时,BeanFactory会调用destroy方法;
  • 一个org.springframework.context.ApplicationContext应当在关闭时,根据应用的生命周期销毁其所有的单例对象;
  • Spring 管理的 Bean 也可以实现 Java 的AutoCloseable接口达到同样的目的;
  • 另一种替代方案是指定自定义销毁方法(例如在 XML Bean 定义中配置destroy-method属性)。

1.2 接口源码

/** * 要实现此接口的 bean 在销毁时希望释放资源。 * 当单独销毁作用域内的 bean 时,BeanFactory 会调用 destroy 方法。 * 一个 org.springframework.context.ApplicationContext 应当在关闭时, * 根据应用的生命周期来销毁其所有的单例对象。 * * Spring 管理的 bean 也可以实现 Java 的 AutoCloseable 接口以达到同样的目的。 * 实现接口的另一种选择是指定一个自定义的销毁方法,例如在 XML bean 定义中。 * 关于所有 bean 生命周期方法的列表,参见 BeanFactory BeanFactory。 * * @author Juergen Hoeller * @since 12.08.2003 * @see InitializingBean * @see org.springframework.beans.factory.support.RootBeanDefinition#getDestroyMethodName() * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#destroySingletons() * @see org.springframework.context.ConfigurableApplicationContext#close() */ public interface DisposableBean { /** * 当 bean 被其包含的 BeanFactory 销毁时调用。 * @throws Exception 如果在关闭时出现错误。错误会被记录, * 但不会被重新抛出,以允许其他 beans 正确释放他们的资源。 */ void destroy() throws Exception; }

接口要点:

  • 接口只有一个destroy()方法,抛出Exception,但容器会捕获并记录异常而不会重新抛出,以允许其他 Bean 继续正确释放资源;
  • destroy()方法在 Bean 被其包含的BeanFactory销毁时调用;
  • @see注释明确指出了相关联的类:RootBeanDefinition#getDestroyMethodName()(自定义销毁方法名)、ConfigurableBeanFactory#destroySingletons()(销毁单例)、ConfigurableApplicationContext#close()(关闭上下文)。

1.3 主要功能

销毁回调:当 Bean 被 Spring 容器销毁时,如果它实现了DisposableBean接口,容器会自动调用其destroy()方法,为 Bean 提供在销毁之前执行必要清理操作的机会(如关闭数据库连接、释放线程池、注销服务等)。

二、最佳实践:可复制的完整示例

本模块代码位于 spring-interface/spring-interface-disposableBean,运行环境基于 JDK 11 与 Spring 5.2.15.RELEASE(见根 pom.xml 中<spring.version>5.2.15.RELEASE</spring.version>)。

2.1 启动类入口

上下文环境使用AnnotationConfigApplicationContext(基于 Java 注解配置 Spring 容器的方式),构造参数给定MyConfiguration组件类,最后调用context.close()方法关闭容器:

public class DisposableBeanApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); context.close(); } }

对应源码见 DisposableBeanApplication.java。

2.2 Java 配置类

使用@Bean注解定义 Bean,确保MyDisposableBean被 Spring 容器管理。注意这里使用public static修饰工厂方法,意味着该 Bean 通过静态工厂方法创建:

@Configuration public class MyConfiguration { @Bean public static MyDisposableBean myDisposableBean(){ return new MyDisposableBean(); } }

对应源码见 MyConfiguration.java。

2.3 实现 DisposableBean 的 Bean

MyDisposableBean类在实例化时建立一个模拟的数据库连接,在销毁时关闭这个连接:

public class MyDisposableBean implements DisposableBean { // 模拟的数据库连接对象 private String databaseConnection; public MyDisposableBean() { // 在构造函数中模拟建立数据库连接 this.databaseConnection = "Database connection established"; System.out.println(databaseConnection); } @Override public void destroy() throws Exception { // 在 destroy 方法中模拟关闭数据库连接 databaseConnection = null; System.out.println("Database connection closed"); } }

对应源码见 MyDisposableBean.java。

2.4 运行结果

运行结果发现,当MyDisposableBeanBean 被销毁时,destroy()方法确实被调用了,并模拟关闭了数据库连接:

Database connection established Database connection closed

2.5 运行方式

该模块是 Maven 多模块项目spring-interface的子模块(见 spring-interface/pom.xml),可直接在模块目录下执行mvn compile exec:java或直接在 IDE 中运行DisposableBeanApplication的main方法,观察控制台输出顺序。

三、时序图:容器关闭到 Bean 销毁的完整链路

下述时序图完整刻画了从应用请求关闭上下文,到最终执行自定义销毁逻辑的全部参与者与调用顺序(原图见 README.md):

四、源码分析:销毁调用链逐层拆解

本节沿着时序图,逐层分析从context.close()到MyDisposableBean.destroy()的完整调用链。

4.1 AbstractApplicationContext#close:同步关闭入口

在org.springframework.context.support.AbstractApplicationContext#close方法中,首先启动一个同步块,同步在startupShutdownMonitor对象上,确保在给定时刻只有一个线程可以执行关闭逻辑,防止多线程导致的资源竞争或数据不一致;随后调用doClose方法,最后移除已注册的 JVM 关闭钩子(因为上下文已被显式关闭):

@Override public void close() { synchronized (this.startupShutdownMonitor) { doClose(); // If we registered a JVM shutdown hook, we don't need it anymore now: // We've already explicitly closed the context. if (this.shutdownHook != null) { try { Runtime.getRuntime().removeShutdownHook(this.shutdownHook); } catch (IllegalStateException ex) { // ignore - VM is already shutting down } } } }

4.2 AbstractApplicationContext#doClose:执行关闭逻辑

在org.springframework.context.support.AbstractApplicationContext#doClose方法中,调用了destroyBeans方法:

protected void doClose() { // ... [代码部分省略以简化] // Destroy all cached singletons in the context's BeanFactory. destroyBeans(); // ... [代码部分省略以简化] }

4.3 AbstractApplicationContext#destroyBeans:委托 BeanFactory

在org.springframework.context.support.AbstractApplicationContext#destroyBeans方法中,首先调用getBeanFactory()返回 Spring 的BeanFactory,然后在其上调用destroySingletons方法,销毁所有在BeanFactory中缓存的单例 Beans:

protected void destroyBeans() { getBeanFactory().destroySingletons(); }

4.4 DefaultListableBeanFactory#destroySingletons:先调用父类逻辑

在org.springframework.beans.factory.support.DefaultListableBeanFactory#destroySingletons方法中,首先调用父类的destroySingletons方法,确保继承自父类的销毁逻辑得到执行,随后清理手动单例名称集合并清空 byType 缓存:

@Override public void destroySingletons() { super.destroySingletons(); updateManualSingletonNames(Set::clear, set -> !set.isEmpty()); clearByTypeCache(); }

4.5 DefaultSingletonBeanRegistry#destroySingletons:逆序销毁

在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingletons方法中,首先在disposableBeans字段上,从其键集合中获取所有 Bean 名称并转换为字符串数组。disposableBeans可能包含实现了DisposableBean接口的 Beans,这些 Beans 需要在容器销毁时特殊处理。最后倒序循环,从最后一个开始销毁。这样做是为了确保依赖关系正确处理:先创建的 Bean 应该后被销毁:

public void destroySingletons() { // ... [代码部分省略以简化] String[] disposableBeanNames; synchronized (this.disposableBeans) { disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet()); } for (int i = disposableBeanNames.length - 1; i >= 0; i--) { destroySingleton(disposableBeanNames[i]); } // ... [代码部分省略以简化] }

4.6 DefaultListableBeanFactory#destroySingleton:逐级下放

在org.springframework.beans.factory.support.DefaultListableBeanFactory#destroySingleton方法中,同样先调用父类的destroySingleton方法,然后移除手动单例名称并清空 byType 缓存:

@Override public void destroySingleton(String beanName) { super.destroySingleton(beanName); removeManualSingletonName(beanName); clearByTypeCache(); }

4.7 DefaultSingletonBeanRegistry#destroySingleton:取出并销毁

在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingleton方法中,使用synchronized在disposableBeans对象上同步,确保多线程环境下安全地访问和修改;先从disposableBeans集合中移除指定名称的 Bean 并转换为DisposableBean类型,再调用destroyBean方法执行实际销毁:

public void destroySingleton(String beanName) { // Remove a registered singleton of the given name, if any. removeSingleton(beanName); // Destroy the corresponding DisposableBean instance. DisposableBean disposableBean; synchronized (this.disposableBeans) { disposableBean = (DisposableBean) this.disposableBeans.remove(beanName); } destroyBean(beanName, disposableBean); }

4.8 DefaultSingletonBeanRegistry#destroyBean:执行实际销毁

在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroyBean方法中,直接调用 Bean 的destroy方法。因为 Bean 是DisposableBean类型的实例,所以一定有destroy方法,该方法提供了 Bean 的自定义销毁逻辑:

protected void destroyBean(String beanName, @Nullable DisposableBean bean) { // ... [代码部分省略以简化] // Actually destroy the bean now... if (bean != null) { try { bean.destroy(); } catch (Throwable ex) { // ... [代码部分省略以简化] } } // ... [代码部分省略以简化] }

4.9 DisposableBeanAdapter#destroy:适配器统一入口

在org.springframework.beans.factory.support.DisposableBeanAdapter#destroy方法中,首先检查invokeDisposableBean变量,如果为true,则直接调用实现DisposableBean接口的 Bean 的destroy方法:

@Override public void destroy() { // ... [代码部分省略以简化] if (this.invokeDisposableBean) { if (logger.isTraceEnabled()) { logger.trace("Invoking destroy() on bean with name '" + this.beanName + "'"); } try { if (System.getSecurityManager() != null) { // ... [代码部分省略以简化] } else { ((DisposableBean) this.bean).destroy(); } } catch (Throwable ex) { // ... [代码部分省略以简化] } } }

从源码结构可以推断:DisposableBeanAdapter是 Spring 内部用于统一销毁逻辑的适配器,它根据 Bean 定义中是否声明了DisposableBean实现、destroy-method名称、AutoCloseable实现等多种情况决定是否以及如何调用销毁逻辑,invokeDisposableBean只是其中一个开关。

4.10 最终执行到自定义逻辑

最后执行到我们自定义的逻辑中:MyDisposableBean类在实例化时建立模拟的数据库连接,在销毁时关闭这个连接。由此形成了完整的“资源建立 → 容器关闭 → 逆序回调 → 资源释放”生命周期闭环。

五、注意事项:生产环境使用要点

  1. 不要过度依赖:尽管DisposableBean提供了一种定义销毁逻辑的标准方法,但更推荐使用@PreDestroy注解或在 Bean 定义中指定destroy-method属性。这些方法通常更简单、更具声明性,并且避免了不必要的代码耦合。仓库中 spring-jsr250-preDestroy 模块专门演示了@PreDestroy的完整用法。

  2. 原型 Bean:对于原型作用域的 Beans,Spring 不会管理它们的完整生命周期。这意味着对于原型 Beans,DisposableBean的destroy()方法不会被自动调用。应确保通过其他方式处理这些 Bean 的资源释放。

  3. 与初始化方法配合:如果使用DisposableBean处理销毁逻辑,可能也会考虑使用InitializingBean处理初始化逻辑(其afterPropertiesSet()方法在属性设置完毕后、Bean 使用前被调用)。但同样,推荐使用@PostConstruct注解或init-method属性定义初始化方法。仓库中 spring-interface-initializingBean 模块给出了完整示例,可对照学习初始化与销毁两个生命周期钩子的对称设计。

  4. 避免重复代码:如果多个 Beans 具有相似的销毁逻辑,考虑将这些逻辑提取到一个共享方法或帮助类中,以减少重复代码并增强可维护性。

  5. 避免长时间运行的操作:destroy()方法应该快速执行并释放资源。避免在其中执行可能耗时的操作,因为这可能会延迟应用的关闭过程。

六、总结

6.1 最佳实践总结

  1. 应用启动:创建启动类DisposableBeanApplication,初始化基于注解的应用上下文AnnotationConfigApplicationContext,该上下文根据MyConfiguration类配置 Spring 容器。
  2. Java 配置:在MyConfiguration类中使用@Bean注解定义名为myDisposableBean的 Bean,确保MyDisposableBean实例被 Spring 容器管理。
  3. 资源管理:MyDisposableBean模拟数据库的连接和断开过程——实例化时模拟建立连接,销毁时(因上下文关闭)调用destroy()方法模拟关闭连接。
  4. 应用关闭:main方法最后调用context.close()关闭应用上下文,容器销毁所有单例 Beans,期间MyDisposableBean的destroy()方法被调用,模拟关闭数据库连接。
  5. 输出结果:先输出"Database connection established"(构造函数中建立模拟连接),随后输出"Database connection closed"(destroy()中关闭模拟连接),验证销毁回调按预期触发。

6.2 源码分析总结

  1. 启动和关闭:创建DisposableBeanApplication启动类,初始化基于注解的应用上下文AnnotationConfigApplicationContext;通过传递MyConfiguration类作为构造参数配置 Spring 容器;使用context.close()关闭上下文,触发资源清理和释放。
  2. 关闭的同步操作:AbstractApplicationContext#close中启动同步块,确保任意时刻只有一个线程关闭上下文,防止资源竞争或数据不一致;关闭后移除任何先前注册的 JVM 关闭钩子。
  3. 实际关闭操作:AbstractApplicationContext#doClose中调用destroyBeans销毁容器中的 Beans。
  4. 销毁 Beans:通过AbstractApplicationContext#destroyBeans,BeanFactory调用其destroySingletons销毁所有缓存的单例 Beans;DefaultListableBeanFactory首先确保父类销毁逻辑执行;DefaultSingletonBeanRegistry#destroySingletons获取所有需要销毁的 Bean 名称,并以创建相反的顺序销毁,正确处理依赖关系。
  5. 具体的 Bean 销毁:对每个要销毁的 Bean 调用DefaultSingletonBeanRegistry#destroySingleton;若 Bean 实现了DisposableBean接口,其destroy方法被调用;为保证线程安全,操作disposableBeans的许多环节都在同步块中执行。
  6. 自定义销毁逻辑:最终到达MyDisposableBean类,其实例销毁时模拟关闭数据库连接。

6.3 与其他生命周期机制的横向对照

销毁机制侵入性声明位置适用场景仓库参考模块
DisposableBean#destroy()高(实现接口)Bean 类内部需要标准回调、且可接受框架耦合spring-interface-disposableBean
@PreDestroy(JSR-250)低(方法注解)Bean 类内部声明式销毁,推荐首选spring-jsr250-preDestroy
destroy-method/destroyMethod无(配置声明)XML 或@Bean属性无侵入复用现有方法RootBeanDefinition#getDestroyMethodName()(源码@see)
AutoCloseable#close()低(实现 JDK 接口)Bean 类内部JDK 标准资源管理接口源码注释提及

整体来看,DisposableBean是理解 Spring 生命周期管理的入门钥匙:它以最直接的方式揭示了容器关闭时单例 Bean 的逆序销毁、同步保护、适配器转发三大设计要点。生产代码中推荐优先使用@PreDestroy或destroy-method等低耦合方案,而将DisposableBean作为理解原理与阅读框架源码的基石。

  • 示例工程
  • 文档

【免费下载链接】spring-reading

涵盖了 Spring 框架的核心概念和关键功能,包括控制反转(IOC)容器的使用,面向切面编程(AOP)的原理与实践,事务管理的方式与实现,Spring MVC 的流程与控制器工作机制,以及 Spring 中数据访问、安全、Boot 自动配置等方面的深入研究。此外,它还包含了 Spring 事件机制的应用、高级主题如缓存抽象和响应式编程,以及对 Spring 源码的编程风格与设计模式的深入探讨。

项目地址:https://gitcode.com/GitHub_Trending/sp/spring-reading
点击查看免费下载

相关推荐

上一篇:Sunshine 串流服务器搭建全攻略:零基础把电脑游戏搬进手机与电视的完整方案
下一篇:Sunshine游戏串流服务器零基础部署避坑指南:自建Moonlight服务端完整上手

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 13:45:20

多项式拟合正弦曲线:机器学习入门实验,理解过拟合与正则化

简介&#xff1a;这份资源面向机器学习初学者与课程实验学习者&#xff0c;围绕多项式拟合正弦曲线这一经典课题&#xff0c;提供完整的Python实现与实验报告。内容涵盖最小二乘法解析解、带2范数惩罚项的正则化优化、梯度下降与共轭梯度法的手写实现&#xff0c;并引导读者通过…

作者头像 李华
网站建设 2026/10/3 13:28:48

【LSSVM回归预测】基于matlab蝙蝠算法优化最小二乘支持向量机BA-LSSVM回归预测【含Matlab源码 109期】

⛄一、运行结果 ✅博主简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,Matlab项目合作可私信。 🍎个人主页:海神之光 🏆代码获取方式: 海神之光Matlab王者学习之路—代码获取方式 ⛳️座右铭:行百里者,半于九十。 更多Matlab仿真内容点击👇 Matlab图像处理…

作者头像 李华