spring-bean的销毁流程
1 引入
在 Spring 框架中,Bean 的生命周期管理是其核心功能之一,而 Bean 的注销(Destruction)是生命周期的最后一步。无论是关闭数据库连接、释放线程资源,还是执行缓存持久化操作,合适的销毁策略都至关重要。Spring 为 Bean 提供了多种注销方式,包括实现特定接口、注解配置和 XML 配置等,同时通过源码设计保证了销毁操作的安全性和灵活性。本文将深入剖析 Spring Bean 注销的原理和实现细节,并结合源码探讨其调用流程和应用场景。
2 销毁方法的注册
2.1 入口
核心类:AbstractBeanFactory.java 核心方法:registerDisposableBeanIfNecessary
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);// 单例bean并且是否配置了注销方法if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {if (mbd.isSingleton()) {// Register a DisposableBean implementation that performs all destruction// work for the given bean: DestructionAwareBeanPostProcessors,// DisposableBean interface, custom destroy method.//注册注销方法registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));}else {// A bean with a custom scope...Scope scope = this.scopes.get(mbd.getScope());if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");}scope.registerDestructionCallback(beanName, new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));}}
}
2.2 销毁方法的判断
protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {// bean不能为nullBean// 是否实现DisposableBean接口或者AutoCloseable接口// 是否配置了关于bean销毁的BeanPostProcessorreturn (bean.getClass() != NullBean.class && (DisposableBeanAdapter.hasDestroyMethod(bean, mbd) ||(hasDestructionAwareBeanPostProcessors() && DisposableBeanAdapter.hasApplicableProcessors(bean, getBeanPostProcessorCache().destructionAware))));
}
2.2.1 DisposableBean接口或者AutoCloseable接口
public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {if (bean instanceof DisposableBean || bean instanceof AutoCloseable) {return true;}// 推断注销方法return inferDestroyMethodIfNecessary(bean, beanDefinition) != null;
}private static String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {String destroyMethodName = beanDefinition.resolvedDestroyMethodName;if (destroyMethodName == null) {destroyMethodName = beanDefinition.getDestroyMethodName();// 如果配置的销毁方法名为(inferred) 或者实现了AutoCloseable接口 或者没有配置销毁方法名称if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||(destroyMethodName == null && bean instanceof AutoCloseable)) {// Only perform destroy method inference or Closeable detection// in case of the bean not explicitly implementing DisposableBeandestroyMethodName = null;if (!(bean instanceof DisposableBean)) {try {// 是否配置了名为close的方法destroyMethodName = bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();}catch (NoSuchMethodException ex) {try {// 是否配置了名为shutdown方法destroyMethodName = bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();}catch (NoSuchMethodException ex2) {// no candidate destroy method found}}}}beanDefinition.resolvedDestroyMethodName = (destroyMethodName != null ? destroyMethodName : "");}return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null);
}
2.2.2 DestructionAwareBeanPostProcessor
public static boolean hasApplicableProcessors(Object bean, List<DestructionAwareBeanPostProcessor> postProcessors) {if (!CollectionUtils.isEmpty(postProcessors)) {for (DestructionAwareBeanPostProcessor processor : postProcessors) {if (processor.requiresDestruction(bean)) {return true;}}}return false;
}//实现类:InitDestroyAnnotationBeanPostProcessor.java
public boolean requiresDestruction(Object bean) {// 判断是否配置了bean销毁的后置处理器return findLifecycleMetadata(bean.getClass()).hasDestroyMethods();
}private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {// 缓存中没有 就去构建一份if (this.lifecycleMetadataCache == null) {// Happens after deserialization, during destruction...return buildLifecycleMetadata(clazz);}// Quick check on the concurrent map first, with minimal locking.LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);if (metadata == null) {synchronized (this.lifecycleMetadataCache) {metadata = this.lifecycleMetadataCache.get(clazz);if (metadata == null) {metadata = buildLifecycleMetadata(clazz);this.lifecycleMetadataCache.put(clazz, metadata);}return metadata;}}return metadata;
}private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {return this.emptyLifecycleMetadata;}List<LifecycleElement> initMethods = new ArrayList<>();List<LifecycleElement> destroyMethods = new ArrayList<>();Class<?> targetClass = clazz;do {final List<LifecycleElement> currInitMethods = new ArrayList<>();final List<LifecycleElement> currDestroyMethods = new ArrayList<>();// 遍历方法ReflectionUtils.doWithLocalMethods(targetClass, method -> {// 判断是否存在@postConstructif (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {LifecycleElement element = new LifecycleElement(method);currInitMethods.add(element);if (logger.isTraceEnabled()) {logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);}}// 判断是否存在@PreDestroyif (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {currDestroyMethods.add(new LifecycleElement(method));if (logger.isTraceEnabled()) {logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);}}});// 父类的在前面initMethods.addAll(0, currInitMethods);destroyMethods.addAll(currDestroyMethods);// 遍历父类targetClass = targetClass.getSuperclass();}while (targetClass != null && targetClass != Object.class);return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :new LifecycleMetadata(clazz, initMethods, destroyMethods));
}
3 执行销毁方法
核心类:AbstractApplicationContext.java 核心方法:doClose
protected void doClose() {// Check whether an actual close attempt is necessary...if (this.active.get() && this.closed.compareAndSet(false, true)) {if (logger.isDebugEnabled()) {logger.debug("Closing " + this);}if (!NativeDetector.inNativeImage()) {LiveBeansView.unregisterApplicationContext(this);}try {// Publish shutdown event.publishEvent(new ContextClosedEvent(this));}catch (Throwable ex) {logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);}// Stop all Lifecycle beans, to avoid delays during individual destruction.if (this.lifecycleProcessor != null) {try {this.lifecycleProcessor.onClose();}catch (Throwable ex) {logger.warn("Exception thrown from LifecycleProcessor on context close", ex);}}// Destroy all cached singletons in the context's BeanFactory.//销毁所有的beandestroyBeans();// Close the state of this context itself.closeBeanFactory();// Let subclasses do some final clean-up if they wish...onClose();// Reset local application listeners to pre-refresh state.if (this.earlyApplicationListeners != null) {this.applicationListeners.clear();this.applicationListeners.addAll(this.earlyApplicationListeners);}// Switch to inactive.this.active.set(false);}
}protected void destroyBeans() {getBeanFactory().destroySingletons();
}public void destroySingletons() {if (logger.isTraceEnabled()) {logger.trace("Destroying singletons in " + this);}synchronized (this.singletonObjects) {this.singletonsCurrentlyInDestruction = true;}String[] disposableBeanNames;synchronized (this.disposableBeans) {disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());}for (int i = disposableBeanNames.length - 1; i >= 0; i--) {// 销毁beandestroySingleton(disposableBeanNames[i]);}this.containedBeanMap.clear();this.dependentBeanMap.clear();this.dependenciesForBeanMap.clear();clearSingletonCache();
}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);
}protected void destroyBean(String beanName, @Nullable DisposableBean bean) {// dependentBeanMap表示某bean被哪些bean依赖了// 所以现在要销毁某个bean时,如果这个Bean还被其他Bean依赖了,那么也得销毁其他Bean// Trigger destruction of dependent beans first...Set<String> dependencies;synchronized (this.dependentBeanMap) {// Within full synchronization in order to guarantee a disconnected Setdependencies = this.dependentBeanMap.remove(beanName);}if (dependencies != null) {if (logger.isTraceEnabled()) {logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);}for (String dependentBeanName : dependencies) {destroySingleton(dependentBeanName);}}// Actually destroy the bean now...if (bean != null) {try {// 调用销毁方法bean.destroy();}catch (Throwable ex) {if (logger.isWarnEnabled()) {logger.warn("Destruction of bean with name '" + beanName + "' threw an exception", ex);}}}// Trigger destruction of contained beans...Set<String> containedBeans;synchronized (this.containedBeanMap) {// Within full synchronization in order to guarantee a disconnected SetcontainedBeans = this.containedBeanMap.remove(beanName);}if (containedBeans != null) {for (String containedBeanName : containedBeans) {destroySingleton(containedBeanName);}}// Remove destroyed bean from other beans' dependencies.synchronized (this.dependentBeanMap) {for (Iterator<Map.Entry<String, Set<String>>> it = this.dependentBeanMap.entrySet().iterator(); it.hasNext();) {Map.Entry<String, Set<String>> entry = it.next();Set<String> dependenciesToClean = entry.getValue();dependenciesToClean.remove(beanName);if (dependenciesToClean.isEmpty()) {it.remove();}}}// Remove destroyed bean's prepared dependency information.this.dependenciesForBeanMap.remove(beanName);
}//实现类:DisposableBeanAdapter.java
public void destroy() {if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {// 如果类型是DestructionAwareBeanPostProcessorfor (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {processor.postProcessBeforeDestruction(this.bean, this.beanName);}}if (this.invokeDisposableBean) {if (logger.isTraceEnabled()) {logger.trace("Invoking destroy() on bean with name '" + this.beanName + "'");}try {// 实现了DisposableBean接口if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {((DisposableBean) this.bean).destroy();return null;}, this.acc);}else {((DisposableBean) this.bean).destroy();}}catch (Throwable ex) {String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";if (logger.isDebugEnabled()) {logger.warn(msg, ex);}else {logger.warn(msg + ": " + ex);}}}// 执行推断的销毁方法if (this.destroyMethod != null) {invokeCustomDestroyMethod(this.destroyMethod);}else if (this.destroyMethodName != null) {Method methodToInvoke = determineDestroyMethod(this.destroyMethodName);if (methodToInvoke != null) {invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke));}}
}
4 总结
-
在bean初始化后 会执行销毁方法的注册 注册主要分为两类 一类是实现接口 一类是通过bean的后置处理器
-
实现DisposableBean接口或者AutoCloseable接口 会直接注册
-
如果配置了DestructionAwareBeanPostProcessor 并且校验方法返回true 也会添加到注册流程
-
在容器关闭时 会注销bean 注销时会将注册bean的销毁方法容器取出遍历执行对应的销毁方法
相关文章:
spring-bean的销毁流程
1 引入 在 Spring 框架中,Bean 的生命周期管理是其核心功能之一,而 Bean 的注销(Destruction)是生命周期的最后一步。无论是关闭数据库连接、释放线程资源,还是执行缓存持久化操作,合适的销毁策略都至关重…...
问:Spring MVC DispatcherServlet流程步骤梳理
DispatcherServlet是Spring MVC框架中的核心组件,负责接收客户端请求并将其分发到相应的控制器进行处理。作为前端控制器(Front Controller)的实现,DispatcherServlet在整个请求处理流程中扮演着至关重要的角色。本文将探讨Dispat…...
用源码编译虚幻引擎,并打包到安卓平台
用源码编译虚幻引擎,并打包到安卓平台 前往我的博客,获取更优的阅读体验 作业内容: 源码编译UE5.4构建C项目,简单设置打包到安卓平台 编译虚幻 5 前置内容 这里需要将 Epic 账号和 Github 账号绑定,然后加入 Epic 邀请的组织,…...
快速搭建Android开发环境:Docker部署docker-android并实现远程连接
目录 前言 1. 虚拟化环境检查 2. Android 模拟器部署 3. Ubuntu安装Cpolar 4. 配置公网地址 5. 远程访问 小结 6. 固定Cpolar公网地址 7. 固定地址访问 作者简介: 懒大王敲代码,计算机专业应届生 今天给大家聊聊快速搭建Android开发环境&#x…...
「Mac玩转仓颉内测版21」基础篇1 - 仓颉程序的基本组成
本篇将系统介绍Cangjie编程语言中程序的基本组成部分,涵盖 main 函数的定义、包与模块的使用、变量类型、作用域和代码结构原则,帮助开发者理解Cangjie程序的整体结构。 关键词 程序入口点main函数包与模块变量类型与作用域值类型与引用类型代码结构与规…...
【Linux网络编程】简单的UDP套接字
目录 一,socket编程的相关说明 1-1,sockaddr结构体 1-2,Socket API 二,基于Udp协议的简单通信 三,UDP套接字的应用 3-1,实现英译汉字典 一,socket编程的相关说明 Socket编程是一种网络通信…...
在Vue中使用Excalidraw实现在线画板
概述 Excalidraw是一个非常好用的画图板工具,但是是用React写的,本文分享一种在Vue项目中使用的方法。 效果 实现 Excalidraw简介 这篇文章(Excalidraw 完美的绘图工具:https://zhuanlan.zhihu.com/p/684940131)介绍的很全面,…...
游戏+AI的发展历程,AI技术在游戏行业的应用有哪些?
人工智能(AI)与游戏的结合,不仅是技术进步的体现,更是人类智慧的延伸。从最初的简单规则到如今的复杂决策系统,AI在游戏领域的发展历史可谓波澜壮阔。 早在2001年,就有研究指出游戏人工智能领域࿰…...
Methode Electronics EDI 需求分析
Methode Electronics 是一家总部位于美国的全球性技术公司,专注于设计和制造用于多个行业的电子和电气组件,产品涵盖汽车、工业、电信、医疗设备以及消费电子等多个领域,提供创新的解决方案。 填写Methode_EDI_Parameters_Template Methode_…...
2023AE软件、Adobe After Effects安装步骤分享教程
2023AE软件是一款由Adobe公司开发的视频编辑软件,也被称为Adobe After Effects。它在广告、电影、电视和网络视频等领域广泛应用,用于制作动态图形、特效、合成和其他视觉效果。该软件支持多种视频和音频文件格式,具有丰富的插件和预设&#…...
【前端】JavaScript 变量引用、内存与数组赋值:深入解析三种情景
博客主页: [小ᶻZ࿆] 本文专栏: 前端 文章目录 💯前言💯场景一:直接赋值与重新引用为什么结果不是 [3, 4, 5]?1. 引用与赋值的基本概念2. 图示分析 关键总结 💯场景二:引用指向的变化为什么…...
本地项目运行提示跨域问题
项目背景:我使用phpwebstudy在本地搭建了一个项目,然后前端是http://localhost:8080/ 后端我直接创建了一个本地域名,例如www.abc.com 然后vue.config.js配置如下,这个配置在我所有线上环境是没有任何问题的 devServer: {proxy…...
C++ —— string类(上)
目录 string的介绍 string类功能的使用介绍 constructor —— 构造 介绍使用(1)(2)(4) :构造、拷贝构造、带参构造 介绍(3):拷贝string类对象的一部分字符…...
React Native Mac 环境搭建
下载 Mac 版Android Studio 下载 安装 JDK 环境 Flutter 项目实战-环境变量配置一 安装 Node.js 方式一 通过Node.js 官网下载 下载完成后点击安装包进行安装 安装完成...
Python Web 开发的路径管理艺术:FastAPI 项目中的最佳实践与问题解析20241119
Python Web 开发的路径管理艺术:FastAPI 项目中的最佳实践与问题解析 引言:从路径错误到模块化管理的技术旅程 在现代 Python Web 开发中,路径管理是一个常常被忽视却非常重要的问题。尤其是在使用像 FastAPI 和 Tortoise ORM 这样的框架时…...
Rust derive macro(Rust #[derive])Rust派生宏
参考文章:附录 D:派生特征 trait 文章目录 Rust 中的派生宏 #[derive]基础使用示例:派生 Debug 派生其他常用特征示例:派生 Clone 和 Copy 派生宏的限制和自定义派生自定义派生宏上面代码运行时报错了,以下是解释 结论…...
springboot嗨玩旅游网站
摘 要 嗨玩旅游网站是一个专为旅行爱好者打造的在线平台。我们提供丰富多样的旅游目的地信息,包括景点信息、旅游线路、商品信息、社区信息、活动推广等,帮助用户轻松规划行程。嗨玩旅游网站致力于为用户提供便捷、实用的旅行服务,让每一次旅…...
杰发科技AC7840——EEP中RAM的配置
sample和手册中示例代码的sram区地址定义不一样 这个在RAM中使用没有限制,根据这个表格留下足够空间即可 比如需要4096字节的eep空间,可以把RAM的地址改成E000,即E000-EFFF,共4096bytes即可。...
从零开始的c++之旅——map_set的使用
1.序列式容器和关联式容器 序列式容器:逻辑结构为线性序列的数据结构,两个位置之间没有紧密的关系,比如两者交换一下还是序列式的容器,例如string,vector,deque,array等。 关联式容器࿱…...
Docker中的一些常用命令
find / -type f -name “文件名” 2>/dev/null 寻找所有目录中的这个文件 pwd 查看当前目录的地址 docker pull 镜像名 强制拉镜像 docker run 运行docker systemctl daemon-reload 关闭docker systemctl start docker 启动docker systemctl restart docker 重启docker /…...
Anthropic Economic Index: AI对软件开发的影响 — 深度解读
原文: AI’s impact on software development 发布机构: Anthropic 解读日期: 2026年3月25日 一、研究背景与方法论 1.1 研究动机 软件开发工作虽然在现代经济中占比较小,但影响力巨大。过去两年,能够辅助甚至自动化大量编程工作的AI系统的引入&#x…...
SEO_资深从业者的高级SEO策略与实战技巧
前言:SEO的进阶之道 在当今互联网时代,搜索引擎优化(SEO)已经不再是一个简单的任务。对于资深从业者来说,SEO不仅仅是一门技术,更是一门艺术。本文将从多个角度探讨资深从业者的高级SEO策略与实战技巧&…...
Ostrakon-VL-8B模型压缩实践:在有限显存下的部署与推理
Ostrakon-VL-8B模型压缩实践:在有限显存下的部署与推理 你是不是也遇到过这样的情况:好不容易找到一个功能强大的视觉语言大模型,比如最近挺火的Ostrakon-VL-8B,结果一看显存要求,直接傻眼了——动辄需要几十个G的显存…...
模拟OJ1 2 3
判断素数(改错)作者: Turbo时间限制: 1s章节: 循环问题描述给定程序的功能是:判断一个整数是否是素数,若是输出YES,否则输出NO!。请改正程序中的错误,使它能得出正确的结果。注意:不得增行或删行…...
当几何优化遇上时空建模:玩转TTAO-SE-CNN-LSTM黑科技
基于三角形拓扑结构优化算法优化卷积神经网络-长短时记忆网络结合SE注意力机制的数据分类预测(TTAO-SE-CNN-LSTM) 三角形拓扑结构优化算法TTAO优化长短时记忆网络隐藏层神经元数目、初始学习率和L2正则化参数 基于MATLAB环境 替换自己的数据即可 首先通过卷积神经网络提取数据…...
Z-Image-Turbo-辉夜巫女实战落地:高校数字艺术课程AI绘图实验课教案设计
Z-Image-Turbo-辉夜巫女实战落地:高校数字艺术课程AI绘图实验课教案设计 1. 项目背景与教学价值 在数字艺术教育领域,AI绘图技术正逐渐成为重要的教学工具。Z-Image-Turbo-辉夜巫女是基于Z-Image-Turbo模型的Lora版本,专门针对"辉夜巫…...
取水泵站远程监控物联网系统方案
某取水泵站具备河流/水库双水源取水设计,配置调节池实现水量缓冲,同时包括取水泵、电动蝶阀、潜污泵、送/排风机、原水水质检测仪等设备,实现对泵站设备工况的全面监控与智能控制,保障各个子水厂的供水安全与稳定。通过将现场PLC控…...
EcomGPT-7B社区贡献指南:如何在CSDN等技术平台分享你的应用案例
EcomGPT-7B社区贡献指南:如何在CSDN等技术平台分享你的应用案例 1. 为什么你应该分享你的EcomGPT-7B应用经验? 如果你已经用EcomGPT-7B做出了一些有意思的东西,比如一个智能客服机器人、一个商品描述生成器,或者任何能解决实际问…...
Nacos如何开启ssl(https)[图文版]
首先,你得有个域名,只有域名才能有ssl 在你的腾讯云或者阿里云控制台把域名解析到nacos所在的ip上面 等待几分钟,打开cmd, ping 刚才的域名,如果返回的是nacos的ip那说明解析成功了 例如你的域名是 ttvv.com 那就 ping ttvv.com 准备证书文件 你的证书通常是 .pem 和 .key …...
使用LaTeX自动生成伏羲模型气象分析报告
使用LaTeX自动生成伏羲模型气象分析报告 每次跑完伏羲模型,看着那一大堆NetCDF或GRIB格式的预报数据,你是不是也头疼过?数据有了,漂亮的图也画好了,但要把它们整理成一份格式规范、图表清晰、文字描述专业的正式报告&…...
