- 示例工程
- 文档
【免费下载链接】spring-reading
涵盖了 Spring 框架的核心概念和关键功能,包括控制反转(IOC)容器的使用,面向切面编程(AOP)的原理与实践,事务管理的方式与实现,Spring MVC 的流程与控制器工作机制,以及 Spring 中数据访问、安全、Boot 自动配置等方面的深入研究。此外,它还包含了 Spring 事件机制的应用、高级主题如缓存抽象和响应式编程,以及对 Spring 源码的编程风格与设计模式的深入探讨。
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 closed2.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类在实例化时建立模拟的数据库连接,在销毁时关闭这个连接。由此形成了完整的“资源建立 → 容器关闭 → 逆序回调 → 资源释放”生命周期闭环。
五、注意事项:生产环境使用要点
不要过度依赖:尽管
DisposableBean提供了一种定义销毁逻辑的标准方法,但更推荐使用@PreDestroy注解或在 Bean 定义中指定destroy-method属性。这些方法通常更简单、更具声明性,并且避免了不必要的代码耦合。仓库中 spring-jsr250-preDestroy 模块专门演示了@PreDestroy的完整用法。原型 Bean:对于原型作用域的 Beans,Spring 不会管理它们的完整生命周期。这意味着对于原型 Beans,
DisposableBean的destroy()方法不会被自动调用。应确保通过其他方式处理这些 Bean 的资源释放。与初始化方法配合:如果使用
DisposableBean处理销毁逻辑,可能也会考虑使用InitializingBean处理初始化逻辑(其afterPropertiesSet()方法在属性设置完毕后、Bean 使用前被调用)。但同样,推荐使用@PostConstruct注解或init-method属性定义初始化方法。仓库中 spring-interface-initializingBean 模块给出了完整示例,可对照学习初始化与销毁两个生命周期钩子的对称设计。避免重复代码:如果多个 Beans 具有相似的销毁逻辑,考虑将这些逻辑提取到一个共享方法或帮助类中,以减少重复代码并增强可维护性。
避免长时间运行的操作:
destroy()方法应该快速执行并释放资源。避免在其中执行可能耗时的操作,因为这可能会延迟应用的关闭过程。
六、总结
6.1 最佳实践总结
- 应用启动:创建启动类
DisposableBeanApplication,初始化基于注解的应用上下文AnnotationConfigApplicationContext,该上下文根据MyConfiguration类配置 Spring 容器。 - Java 配置:在
MyConfiguration类中使用@Bean注解定义名为myDisposableBean的 Bean,确保MyDisposableBean实例被 Spring 容器管理。 - 资源管理:
MyDisposableBean模拟数据库的连接和断开过程——实例化时模拟建立连接,销毁时(因上下文关闭)调用destroy()方法模拟关闭连接。 - 应用关闭:
main方法最后调用context.close()关闭应用上下文,容器销毁所有单例 Beans,期间MyDisposableBean的destroy()方法被调用,模拟关闭数据库连接。 - 输出结果:先输出
"Database connection established"(构造函数中建立模拟连接),随后输出"Database connection closed"(destroy()中关闭模拟连接),验证销毁回调按预期触发。
6.2 源码分析总结
- 启动和关闭:创建
DisposableBeanApplication启动类,初始化基于注解的应用上下文AnnotationConfigApplicationContext;通过传递MyConfiguration类作为构造参数配置 Spring 容器;使用context.close()关闭上下文,触发资源清理和释放。 - 关闭的同步操作:
AbstractApplicationContext#close中启动同步块,确保任意时刻只有一个线程关闭上下文,防止资源竞争或数据不一致;关闭后移除任何先前注册的 JVM 关闭钩子。 - 实际关闭操作:
AbstractApplicationContext#doClose中调用destroyBeans销毁容器中的 Beans。 - 销毁 Beans:通过
AbstractApplicationContext#destroyBeans,BeanFactory调用其destroySingletons销毁所有缓存的单例 Beans;DefaultListableBeanFactory首先确保父类销毁逻辑执行;DefaultSingletonBeanRegistry#destroySingletons获取所有需要销毁的 Bean 名称,并以创建相反的顺序销毁,正确处理依赖关系。 - 具体的 Bean 销毁:对每个要销毁的 Bean 调用
DefaultSingletonBeanRegistry#destroySingleton;若 Bean 实现了DisposableBean接口,其destroy方法被调用;为保证线程安全,操作disposableBeans的许多环节都在同步块中执行。 - 自定义销毁逻辑:最终到达
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 源码的编程风格与设计模式的深入探讨。
相关推荐
人体姿态搜索系统:5分钟搭建你的智能动作分析平台
人体姿态搜索系统:5分钟搭建你的智能动作分析平台 你是否想过,如何快速搭建一个能够识别和分析人体动作的智能系统?pose search是一个基于Web技术的人体
前端计算机视觉JCSprout 实战:Spring Bean 生命周期完整解析(注解、接口、Aware 与 BeanPostProcessor 五条初始化/销毁路径)
JCSprout 实战:Spring Bean 生命周期完整解析(注解、接口、Aware 与 BeanPostProcessor 五条初始化/销毁路径) Spr
文档知识库后端教程GitHub_Trending/sp/spring-reading源码探秘:Spring Bean生命周期全解析
GitHub_Trending/sp/spring reading源码探秘:Spring Bean生命周期全解析 引言:你还在为Bean生命周期困惑吗? 在Sp
示例工程文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考