尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

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

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.version5.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 的 BeanMyDisposableBean类在实例化时建立一个模拟的数据库连接在销毁时关闭这个连接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 dont need it anymore now: // Weve 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 contexts BeanFactory. destroyBeans(); // ... [代码部分省略以简化] }4.3 AbstractApplicationContext#destroyBeans委托 BeanFactory在org.springframework.context.support.AbstractApplicationContext#destroyBeans方法中首先调用getBeanFactory()返回 Spring 的BeanFactory然后在其上调用destroySingletons方法销毁所有在BeanFactory中缓存的单例 Beansprotected 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对于原型作用域的 BeansSpring 不会管理它们的完整生命周期。这意味着对于原型 BeansDisposableBean的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 closeddestroy()中关闭模拟连接验证销毁回调按预期触发。6.2 源码分析总结启动和关闭创建DisposableBeanApplication启动类初始化基于注解的应用上下文AnnotationConfigApplicationContext通过传递MyConfiguration类作为构造参数配置 Spring 容器使用context.close()关闭上下文触发资源清理和释放。关闭的同步操作AbstractApplicationContext#close中启动同步块确保任意时刻只有一个线程关闭上下文防止资源竞争或数据不一致关闭后移除任何先前注册的 JVM 关闭钩子。实际关闭操作AbstractApplicationContext#doClose中调用destroyBeans销毁容器中的 Beans。销毁 Beans通过AbstractApplicationContext#destroyBeansBeanFactory调用其destroySingletons销毁所有缓存的单例 BeansDefaultListableBeanFactory首先确保父类销毁逻辑执行DefaultSingletonBeanRegistry#destroySingletons获取所有需要销毁的 Bean 名称并以创建相反的顺序销毁正确处理依赖关系。具体的 Bean 销毁对每个要销毁的 Bean 调用DefaultSingletonBeanRegistry#destroySingleton若 Bean 实现了DisposableBean接口其destroy方法被调用为保证线程安全操作disposableBeans的许多环节都在同步块中执行。自定义销毁逻辑最终到达MyDisposableBean类其实例销毁时模拟关闭数据库连接。6.3 与其他生命周期机制的横向对照销毁机制侵入性声明位置适用场景仓库参考模块DisposableBean#destroy()高实现接口Bean 类内部需要标准回调、且可接受框架耦合spring-interface-disposableBeanPreDestroyJSR-250低方法注解Bean 类内部声明式销毁推荐首选spring-jsr250-preDestroydestroy-method/destroyMethod无配置声明XML 或Bean属性无侵入复用现有方法RootBeanDefinition#getDestroyMethodName()源码seeAutoCloseable#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点击查看免费下载相关推荐人体姿态搜索系统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示例工程文档上一篇Sunshine 串流服务器搭建全攻略零基础把电脑游戏搬进手机与电视的完整方案下一篇Sunshine游戏串流服务器零基础部署避坑指南自建Moonlight服务端完整上手创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表