☰
spring-reading 源码解读:DestructionAwareBeanPostProcessor 销毁前回调机制与 Bean 生命周期管理实战
2026/10/3 8:34:14 网站建设 项目流程
  • 示例工程
  • 文档

【免费下载链接】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-5(Spring 5 之前)的DestructionAwareBeanPostProcessor实现没有覆写该方法,Spring 会默默地假设返回值为true,以保持旧版本实现的兼容行为。

四、主要功能

围绕该接口,最核心的功能只有一个,但衍生出多个典型用途:

  1. 销毁前逻辑(核心功能):使用postProcessBeforeDestruction(Object bean, String beanName)方法,为 bean 执行自定义的销毁逻辑。当一个 bean 被容器标记为销毁时,此方法将被调用。典型场景包括:

    • 容器关闭时的资源释放(关闭数据库连接、网络连接、文件流、线程池等);
    • 状态记录(销毁前把最终状态写入日志或存储);
    • 依赖清理(解除循环引用、清理缓存项、通知协作组件等)。
  2. 按需销毁过滤(辅助功能):通过覆写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定义了两个 bean:MyDestructionAwareBeanPostProcessor(自定义销毁后处理器)和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.

三行输出的含义与验证逻辑完全对应:

  1. connection opened.:容器初始化阶段,MyDestructionAwareBeanPostProcessor#postProcessAfterInitialization检测到 bean 是ConnectionServiceImpl实例,调用openConnection(),连接被打开;
  2. Is connected: true:应用运行期,main 方法中调用isConnected()并打印,证明在应用上下文运行期间连接确实处于打开状态;
  3. 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 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 } } } }

7.2 AbstractApplicationContext#doClose —— 委托销毁 beans

doClose()内部调用destroyBeans(),销毁上下文中 BeanFactory 缓存的全部单例 bean:

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

7.3 AbstractApplicationContext#destroyBeans —— 转发到 BeanFactory

destroyBeans()获取 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通常是DisposableBeanAdapter(Spring 为每个待销毁 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 的管理方式。

九、总结

最佳实践总结

  1. 应用启动:在DestructionAwareBeanPostProcessorApplication#main中创建AnnotationConfigApplicationContext,通过MyConfiguration完成配置,获取connectionServicebean 并查询连接状态;
  2. 容器初始化:Spring 容器根据配置创建MyDestructionAwareBeanPostProcessor与ConnectionServiceImpl两个 bean;ConnectionServiceImpl初始化完成后,postProcessAfterInitialization被调用,连接随即打开;
  3. 应用运行期:main 方法打印连接状态为打开,证明 bean 生命周期开始阶段资源已就绪;
  4. 销毁阶段:context.close()触发销毁流程,postProcessBeforeDestruction被调用,closeConnection()执行,连接被关闭;
  5. 运行结果:控制台输出connection opened. / Is connected: true / connection closed.,完整验证了「初始化打开资源、销毁前释放资源」的生命周期闭环。

源码分析总结

  1. 关闭入口:AbstractApplicationContext#close通过同步块保证关闭操作线程安全,调用doClose并移除 JVM 关闭钩子;
  2. 销毁 Beans:doClose内调用destroyBeans,进而调用BeanFactory#destroySingletons;
  3. 销毁单例:destroySingletons取出disposableBeans中所有 bean 名,倒序遍历逐个调用destroySingleton,保证依赖顺序正确;
  4. 执行销毁逻辑:destroySingleton从集合中移除并取出DisposableBean,调用destroyBean执行实际销毁,DisposableBeanAdapter#destroy遍历所有DestructionAwareBeanPostProcessor并回调postProcessBeforeDestruction;
  5. 自定义逻辑落地:最终由自定义处理器中的instanceof判断决定是否执行目标销毁逻辑,完成资源释放。

通过本模块的学习可以看到:DestructionAwareBeanPostProcessor是 Spring 容器销毁阶段最灵活的扩展点之一,配合requiresDestruction过滤机制,可以精确、高效地管理特定 bean 的销毁前行为,是编写优雅停机、资源治理类框架代码时值得优先考虑的生命周期钩子。

  • 示例工程
  • 文档

【免费下载链接】spring-reading

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

项目地址:https://gitcode.com/GitHub_Trending/sp/spring-reading
点击查看免费下载
上一篇:mikro-orm 类型安全关联(Type-Safe Relations):从 Reference 包装器到 Loaded 类型的实战指南
下一篇:Adobe GenP 3.0破解工具:5步快速解锁Adobe全家桶高级功能

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询