ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

spring-reading 源码解读:DestructionAwareBeanPostProcessor 销毁前回调机制与 Bean 生命周期管理实战

spring-reading 源码解读:DestructionAwareBeanPostProcessor 销毁前回调机制与 Bean 生命周期管理实战 示例工程文档【免费下载链接】spring-reading涵盖了 Spring 框架的核心概念和关键功能包括控制反转IOC容器的使用面向切面编程AOP的原理与实践事务管理的方式与实现Spring MVC 的流程与控制器工作机制以及 Spring 中数据访问、安全、Boot 自动配置等方面的深入研究。此外它还包含了 Spring 事件机制的应用、高级主题如缓存抽象和响应式编程以及对 Spring 源码的编程风格与设计模式的深入探讨。项目地址https://gitcode.com/GitHub_Trending/sp/spring-reading点击查看免费下载本文以 spring-reading 仓库中spring-interface-destructionAwareBeanPostProcessor示例模块为核心深入讲解 Spring 框架DestructionAwareBeanPostProcessor接口的设计意图、源码定义与完整销毁调用链并结合可运行的连接资源管理示例帮助读者掌握在 bean 销毁前介入生命周期、释放外部资源的实战方案。读完本文你将能够独立实现一个自定义的销毁前处理器并理解context.close()背后从容器关闭到单个 bean 销毁的完整源码路径。一、模块基本信息与适用场景本模块位于仓库的spring-interface/spring-interface-destructionAwareBeanPostProcessor目录下是 spring-reading 系列中专门讲解「销毁前 Bean 后处理器」的示例工程。它对应的 Maven 工程为spring-interface-destructionAwareBeanPostProcessor父工程为spring-interface完整代码结构如下启动入口DestructionAwareBeanPostProcessorApplication.java配置类MyConfiguration.java自定义处理器MyDestructionAwareBeanPostProcessor.java服务接口与实现ConnectionService.java、ConnectionServiceImpl.java该接口在 Spring 官方源码中位于org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor自 Spring 1.0.1 版本起引入是BeanPostProcessor的一个专门面向销毁阶段的子接口。在真实的 Spring 应用中它最常见的应用场景包括数据库连接池 / 网络连接 / 文件句柄等外部资源的关闭、缓存清理、状态记录、优雅停机等需要在 bean 销毁前执行的收尾逻辑。二、接口描述与核心设计思想DestructionAwareBeanPostProcessor接口用于提供在 bean 销毁之前进行额外处理或操作的机会。它的核心职责是在 bean 即将被销毁时允许容器回调我们自定义的逻辑。理解这个接口需要先建立一条关键认知普通的BeanPostProcessor只提供了 bean 初始化前后postProcessBeforeInitialization/postProcessAfterInitialization两个回调并不包含任何销毁阶段的回调。而DestructionAwareBeanPostProcessor作为它的子接口专门补上了「销毁前」这一环从而让开发者能够在 bean 生命周期的两端都介入自定义逻辑。这与DisposableBean#destroy()方法、PreDestroy注解以及AbstractBeanDefinition#setDestroyMethodName(String)配置的销毁方法共同构成了 Spring 的销毁回调体系。关联阅读仓库中 spring-interface-disposableBean 模块演示了DisposableBean接口的销毁回调spring-jsr250-preDestroy 模块则演示了PreDestroy注解方式的销毁逻辑可对照学习三种销毁回调的执行顺序与差异。三、接口源码逐行解读Spring 官方源码中该接口的完整定义如下/** * BeanPostProcessor的子接口增加了销毁前的回调。 * * 典型的用途是在特定的bean类型上调用自定义的销毁回调 * 与相应的初始化回调相匹配。 * * author Juergen Hoeller * since 1.0.1 */ public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor { /** * 在给定的 bean 实例销毁之前应用此 BeanPostProcessor * 例如调用自定义的销毁回调。 * 与 DisposableBean 的 {code destroy} 方法和一个自定义的销毁方法一样此回调 * 仅适用于容器完全管理其生命周期的 beans。这通常适用于单例和有作用域的 beans。 * param bean 要被销毁的 bean 实例 * param beanName bean 的名称 * throws org.springframework.beans.BeansException 如果发生错误 * see org.springframework.beans.factory.DisposableBean#destroy() * see org.springframework.beans.factory.support.AbstractBeanDefinition#setDestroyMethodName(String) */ void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException; /** * 确定给定的 bean 实例是否需要由此后处理器销毁。 * 默认实现返回true。如果一个基于 pre-5 的 DestructionAwareBeanPostProcessor * 实现没有为此方法提供具体实现Spring 也会默默地假设返回值为 true。 * param bean 要检查的 bean 实例 * return 如果需要为此 bean 实例最终调用 postProcessBeforeDestruction返回 true否则返回 false * since 4.3 */ default boolean requiresDestruction(Object bean) { return true; } }接口包含两个方法各自承担不同的职责1.postProcessBeforeDestruction(Object bean, String beanName)这是接口的核心方法在给定的 bean 实例销毁之前被调用。方法签名中有两个值得注意的要点第一个参数bean是待销毁的 bean 实例实现类通常通过instanceof判断目标类型再执行针对性的收尾逻辑第二个参数beanName是 bean 在容器中的名称方便实现类按名称做精细化处理方法声明抛出BeansException意味着实现中抛出的任何异常都会被传递到销毁调用链中由DefaultSingletonBeanRegistry#destroyBean捕获处理。从 Spring 官方注释可以确认一个重要语义该回调仅适用于容器完全管理其生命周期的 beans这通常指单例singleton和有作用域scoped的 beans。也就是说如果你通过BeanFactory.getBean手动创建、脱离容器管理的实例不会触发该回调。2.requiresDestruction(Object bean)这是一个自 Spring 4.3 版本引入的默认方法用于提前判断给定的 bean 实例是否需要被此后处理器处理。默认返回true。Spring 在底层调用postProcessBeforeDestruction之前会先遍历注册的销毁后处理器并调用该方法做过滤返回false的处理器将被跳过从而避免无意义的销毁回调调用。此外官方注释还给出一个兼容性说明如果一个基于 pre-5Spring 5 之前的DestructionAwareBeanPostProcessor实现没有覆写该方法Spring 会默默地假设返回值为true以保持旧版本实现的兼容行为。四、主要功能围绕该接口最核心的功能只有一个但衍生出多个典型用途销毁前逻辑核心功能使用postProcessBeforeDestruction(Object bean, String beanName)方法为 bean 执行自定义的销毁逻辑。当一个 bean 被容器标记为销毁时此方法将被调用。典型场景包括容器关闭时的资源释放关闭数据库连接、网络连接、文件流、线程池等状态记录销毁前把最终状态写入日志或存储依赖清理解除循环引用、清理缓存项、通知协作组件等。按需销毁过滤辅助功能通过覆写requiresDestruction(Object bean)让处理器只对真正关心的 bean 类型生效既提高了性能也避免了误伤其他 bean 的销毁流程。五、最佳实践连接资源生命周期管理示例本模块用一个非常直观的「连接管理」案例演示了接口的完整用法。下面按代码文件逐一展开代码均可在模块源码中直接找到。5.1 启动入口启动类使用AnnotationConfigApplicationContext基于 Java 注解配置 Spring 容器的方式构造参数传入MyConfiguration配置类随后从上下文中获取名为connectionService的 bean 并打印连接状态最后调用close()关闭上下文触发销毁流程public class DestructionAwareBeanPostProcessorApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(MyConfiguration.class); ConnectionService connection context.getBean(connectionService, ConnectionService.class); System.out.println(Is connected: connection.isConnected()); context.close(); } }对应源码见 DestructionAwareBeanPostProcessorApplication.java。5.2 配置类配置类通过Configuration声明为配置类并用Bean定义了两个 beanMyDestructionAwareBeanPostProcessor自定义销毁后处理器和ConnectionServiceImpl被管理的业务 bean。这里两个 bean 都必须是容器管理的 bean才能保证 Spring 容器在执行销毁流程时能够回调到自定义处理器Configuration public class MyConfiguration { Bean public static MyDestructionAwareBeanPostProcessor myDestructionAwareBeanPostProcessor() { return new MyDestructionAwareBeanPostProcessor(); } Bean public ConnectionService connectionService() { return new ConnectionServiceImpl(); } }对应源码见 MyConfiguration.java。5.3 自定义销毁后处理器MyDestructionAwareBeanPostProcessor实现了DestructionAwareBeanPostProcessor的三个方法其设计意图是管理ConnectionServiceImplbean 的完整生命周期初始化完成后自动打开连接销毁前自动关闭连接确保资源在不再需要时被及时释放public class MyDestructionAwareBeanPostProcessor implements DestructionAwareBeanPostProcessor { Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof ConnectionServiceImpl) { ((ConnectionServiceImpl) bean).openConnection(); } return bean; } Override public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException { if (bean instanceof ConnectionServiceImpl) { ((ConnectionServiceImpl) bean).closeConnection(); } } Override public boolean requiresDestruction(Object bean) { return (bean instanceof ConnectionServiceImpl); } }对应源码见 MyDestructionAwareBeanPostProcessor.java。注意其中postProcessAfterInitialization是从父接口BeanPostProcessor继承而来的初始化后回调——本示例用它模拟「初始化完成后打开连接」与销毁前回调形成首尾呼应这正是接口源码注释中提到的「与相应的初始化回调相匹配」的典型用法。5.4 服务接口与实现ConnectionService接口定义了连接服务的三个操作打开、关闭、查询状态public interface ConnectionService { void openConnection(); void closeConnection(); boolean isConnected(); }对应源码见 ConnectionService.java。ConnectionServiceImpl用布尔字段isConnected模拟连接状态并输出可观察的日志便于验证回调是否真正执行public class ConnectionServiceImpl implements ConnectionService { private boolean isConnected false; Override public void openConnection() { isConnected true; System.out.println(connection opened.); } Override public void closeConnection() { if (isConnected) { isConnected false; System.out.println(connection closed.); } } Override public boolean isConnected() { return isConnected; } }对应源码见 ConnectionServiceImpl.java。5.5 运行结果与行为验证运行DestructionAwareBeanPostProcessorApplication的main方法控制台输出如下connection opened. Is connected: true connection closed.三行输出的含义与验证逻辑完全对应connection opened.容器初始化阶段MyDestructionAwareBeanPostProcessor#postProcessAfterInitialization检测到 bean 是ConnectionServiceImpl实例调用openConnection()连接被打开Is connected: true应用运行期main 方法中调用isConnected()并打印证明在应用上下文运行期间连接确实处于打开状态connection closed.调用context.close()后MyDestructionAwareBeanPostProcessor#postProcessBeforeDestruction检测到 bean 是ConnectionServiceImpl实例调用closeConnection()连接被关闭资源被释放。六、销毁调用链时序图从context.close()到自定义postProcessBeforeDestruction被执行中间跨越了多个 Spring 容器组件。本模块 README 中用 Mermaid 时序图完整刻画了这一调用链整理如下七、源码分析从 close() 到 postProcessBeforeDestruction 的完整调用链7.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 } } } }7.2 AbstractApplicationContext#doClose —— 委托销毁 beansdoClose()内部调用destroyBeans()销毁上下文中 BeanFactory 缓存的全部单例 beanprotected void doClose() { // ... [代码部分省略以简化] // Destroy all cached singletons in the contexts BeanFactory. destroyBeans(); // ... [代码部分省略以简化] }7.3 AbstractApplicationContext#destroyBeans —— 转发到 BeanFactorydestroyBeans()获取 Spring 的BeanFactory并调用其destroySingletons()方法protected void destroyBeans() { getBeanFactory().destroySingletons(); }7.4 DefaultListableBeanFactory#destroySingletons —— 先执行父类销毁逻辑DefaultListableBeanFactory覆写了destroySingletons()先调用父类DefaultSingletonBeanRegistry的销毁逻辑再清理手动注册的单例名缓存和按类型缓存Override public void destroySingletons() { super.destroySingletons(); updateManualSingletonNames(Set::clear, set - !set.isEmpty()); clearByTypeCache(); }7.5 DefaultSingletonBeanRegistry#destroySingletons —— 倒序逐个销毁父类的destroySingletons()首先从disposableBeans字段存放了需要特殊销毁处理的DisposableBean实例的键集合中取出所有 bean 名称并转为字符串数组然后倒序循环从最后一个开始逐个销毁。倒序的目的是保证依赖关系处理正确——先被创建的 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]); } // ... [代码部分省略以简化] }7.6 DefaultListableBeanFactory#destroySingleton —— 覆写并清理缓存DefaultListableBeanFactory#destroySingleton同样先调用父类方法再移除手动单例名并清理按类型缓存Override public void destroySingleton(String beanName) { super.destroySingleton(beanName); removeManualSingletonName(beanName); clearByTypeCache(); }7.7 DefaultSingletonBeanRegistry#destroySingleton —— 移除并转交销毁父类的destroySingleton在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); }7.8 DefaultSingletonBeanRegistry#destroyBean —— 调用适配器的 destroy()destroyBean中直接调用bean.destroy()。这里的bean通常是DisposableBeanAdapterSpring 为每个待销毁 bean 生成的销毁适配器其内部聚合了 bean 的各类销毁回调DisposableBean接口、PreDestroy注解方法、配置的 destroy-method 以及注册的销毁后处理器protected void destroyBean(String beanName, Nullable DisposableBean bean) { // ... [代码部分省略以简化] // Actually destroy the bean now... if (bean ! null) { try { bean.destroy(); } catch (Throwable ex) { // ... [代码部分省略以简化] } } // ... [代码部分省略以简化] }7.9 DisposableBeanAdapter#destroy —— 遍历销毁后处理器这是整个调用链中与DestructionAwareBeanPostProcessor最直接相关的一环DisposableBeanAdapter#destroy()遍历beanPostProcessors集合集合中的每个元素都是DestructionAwareBeanPostProcessor类型逐个调用其postProcessBeforeDestruction(this.bean, this.beanName)Override public void destroy() { if (!CollectionUtils.isEmpty(this.beanPostProcessors)) { for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) { processor.postProcessBeforeDestruction(this.bean, this.beanName); } } // ... [代码部分省略以简化] }从这段源码可以印证两点实现事实其一销毁后处理器是在DisposableBeanAdapter的销毁流程中被统一回调的其二回调发生在适配器继续执行其余销毁逻辑如DisposableBean.destroy()、PreDestroy方法、destroy-method之前即「销毁前」的语义被严格执行。7.10 自定义处理器 —— 最终执行点调用链的终点回到我们的自定义实现com.xcs.spring.config.MyDestructionAwareBeanPostProcessor#postProcessBeforeDestruction。方法中通过instanceof ConnectionServiceImpl判断目标 bean命中则调用closeConnection()确保该类型 bean 在销毁前连接一定被关闭Override public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException { if (bean instanceof ConnectionServiceImpl) { ((ConnectionServiceImpl) bean).closeConnection(); } }八、注意事项与最佳实践1. 性能影响每一个DestructionAwareBeanPostProcessor在 bean 的生命周期结束时都会被调用因此应确保其中的代码高效执行避免在销毁路径上引入不必要的性能瓶颈例如避免在回调中做耗时 IO 或阻塞操作。2. 检查requiresDestruction实现requiresDestruction方法来指定哪些 beans 需要在销毁时处理可以避免无意义的postProcessBeforeDestruction调用从而提高销毁阶段的整体性能。本示例中只对ConnectionServiceImpl类型返回true正是这一实践的体现。3. 异常处理postProcessBeforeDestruction方法中可能抛出任何类型的异常应确保适当处理这些异常避免影响其他 beans 的销毁。从DefaultSingletonBeanRegistry#destroyBean的源码可以看到单个 bean 的销毁异常会被捕获不应让一个 bean 的失败阻断整个容器的关闭流程。4. 确保与其他 BeanPostProcessors 协调如果应用中还有其他BeanPostProcessors需要确保它们之间的相互作用不会导致问题例如多个处理器对同一 bean 的执行顺序、以及它们与销毁回调的叠加效果。5. 与PreDestroy注解协同工作如果 bean 已经使用PreDestroy注解定义了自身的销毁方法这些方法会在postProcessBeforeDestruction被调用之前执行这一点也可以从DisposableBeanAdapter的销毁流程顺序得到印证。确保这两者的逻辑不会互相干扰例如避免对同一资源重复释放。6. 适用范围确认该回调仅适用于容器完全管理其生命周期的 beans单例与有作用域 bean。脱离容器管理的实例不会被回调设计销毁方案时需先确认 bean 的管理方式。九、总结最佳实践总结应用启动在DestructionAwareBeanPostProcessorApplication#main中创建AnnotationConfigApplicationContext通过MyConfiguration完成配置获取connectionServicebean 并查询连接状态容器初始化Spring 容器根据配置创建MyDestructionAwareBeanPostProcessor与ConnectionServiceImpl两个 beanConnectionServiceImpl初始化完成后postProcessAfterInitialization被调用连接随即打开应用运行期main 方法打印连接状态为打开证明 bean 生命周期开始阶段资源已就绪销毁阶段context.close()触发销毁流程postProcessBeforeDestruction被调用closeConnection()执行连接被关闭运行结果控制台输出connection opened. / Is connected: true / connection closed.完整验证了「初始化打开资源、销毁前释放资源」的生命周期闭环。源码分析总结关闭入口AbstractApplicationContext#close通过同步块保证关闭操作线程安全调用doClose并移除 JVM 关闭钩子销毁 BeansdoClose内调用destroyBeans进而调用BeanFactory#destroySingletons销毁单例destroySingletons取出disposableBeans中所有 bean 名倒序遍历逐个调用destroySingleton保证依赖顺序正确执行销毁逻辑destroySingleton从集合中移除并取出DisposableBean调用destroyBean执行实际销毁DisposableBeanAdapter#destroy遍历所有DestructionAwareBeanPostProcessor并回调postProcessBeforeDestruction自定义逻辑落地最终由自定义处理器中的instanceof判断决定是否执行目标销毁逻辑完成资源释放。通过本模块的学习可以看到DestructionAwareBeanPostProcessor是 Spring 容器销毁阶段最灵活的扩展点之一配合requiresDestruction过滤机制可以精确、高效地管理特定 bean 的销毁前行为是编写优雅停机、资源治理类框架代码时值得优先考虑的生命周期钩子。赞分享示例工程文档【免费下载链接】spring-reading涵盖了 Spring 框架的核心概念和关键功能包括控制反转IOC容器的使用面向切面编程AOP的原理与实践事务管理的方式与实现Spring MVC 的流程与控制器工作机制以及 Spring 中数据访问、安全、Boot 自动配置等方面的深入研究。此外它还包含了 Spring 事件机制的应用、高级主题如缓存抽象和响应式编程以及对 Spring 源码的编程风格与设计模式的深入探讨。项目地址https://gitcode.com/GitHub_Trending/sp/spring-reading点击查看免费下载相关推荐Spring DisposableBean 接口深度解析Bean 销毁回调机制与容器关闭生命周期实战spring-readingSpring DisposableBean 接口深度解析Bean 销毁回调机制与容器关闭生命周期实战spring reading DisposableBe示例工程文档GitHub_Trending/sp/spring-reading源码探秘Spring Bean生命周期全解析GitHub_Trending/sp/spring reading源码探秘Spring Bean生命周期全解析 引言你还在为Bean生命周期困惑吗 在Sp示例工程文档DGA-Pool源码剖析线程生命周期管理与Worker销毁机制DGA Pool源码剖析线程生命周期管理与Worker销毁机制 引言动态线程池的痛点与解决方案 在高并发场景下传统线程池ThreadPoolExecut后端上一篇mikro-orm 类型安全关联Type-Safe Relations从 Reference 包装器到 Loaded 类型的实战指南下一篇Adobe GenP 3.0破解工具5步快速解锁Adobe全家桶高级功能创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表