当前位置: 首页 > news >正文

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 总结

  1. 在bean初始化后 会执行销毁方法的注册 注册主要分为两类 一类是实现接口 一类是通过bean的后置处理器

  2. 实现DisposableBean接口或者AutoCloseable接口 会直接注册

  3. 如果配置了DestructionAwareBeanPostProcessor 并且校验方法返回true 也会添加到注册流程

  4. 在容器关闭时 会注销bean 注销时会将注册bean的销毁方法容器取出遍历执行对应的销毁方法

相关文章:

spring-bean的销毁流程

1 引入 在 Spring 框架中&#xff0c;Bean 的生命周期管理是其核心功能之一&#xff0c;而 Bean 的注销&#xff08;Destruction&#xff09;是生命周期的最后一步。无论是关闭数据库连接、释放线程资源&#xff0c;还是执行缓存持久化操作&#xff0c;合适的销毁策略都至关重…...

问:Spring MVC DispatcherServlet流程步骤梳理

DispatcherServlet是Spring MVC框架中的核心组件&#xff0c;负责接收客户端请求并将其分发到相应的控制器进行处理。作为前端控制器&#xff08;Front Controller&#xff09;的实现&#xff0c;DispatcherServlet在整个请求处理流程中扮演着至关重要的角色。本文将探讨Dispat…...

用源码编译虚幻引擎,并打包到安卓平台

用源码编译虚幻引擎&#xff0c;并打包到安卓平台 前往我的博客,获取更优的阅读体验 作业内容: 源码编译UE5.4构建C项目&#xff0c;简单设置打包到安卓平台 编译虚幻 5 前置内容 这里需要将 Epic 账号和 Github 账号绑定&#xff0c;然后加入 Epic 邀请的组织&#xff0c…...

快速搭建Android开发环境:Docker部署docker-android并实现远程连接

目录 前言 1. 虚拟化环境检查 2. Android 模拟器部署 3. Ubuntu安装Cpolar 4. 配置公网地址 5. 远程访问 小结 6. 固定Cpolar公网地址 7. 固定地址访问 作者简介&#xff1a; 懒大王敲代码&#xff0c;计算机专业应届生 今天给大家聊聊快速搭建Android开发环境&#x…...

「Mac玩转仓颉内测版21」基础篇1 - 仓颉程序的基本组成

本篇将系统介绍Cangjie编程语言中程序的基本组成部分&#xff0c;涵盖 main 函数的定义、包与模块的使用、变量类型、作用域和代码结构原则&#xff0c;帮助开发者理解Cangjie程序的整体结构。 关键词 程序入口点main函数包与模块变量类型与作用域值类型与引用类型代码结构与规…...

【Linux网络编程】简单的UDP套接字

目录 一&#xff0c;socket编程的相关说明 1-1&#xff0c;sockaddr结构体 1-2&#xff0c;Socket API 二&#xff0c;基于Udp协议的简单通信 三&#xff0c;UDP套接字的应用 3-1&#xff0c;实现英译汉字典 一&#xff0c;socket编程的相关说明 Socket编程是一种网络通信…...

在Vue中使用Excalidraw实现在线画板

概述 Excalidraw是一个非常好用的画图板工具&#xff0c;但是是用React写的&#xff0c;本文分享一种在Vue项目中使用的方法。 效果 实现 Excalidraw简介 这篇文章(Excalidraw 完美的绘图工具&#xff1a;https://zhuanlan.zhihu.com/p/684940131)介绍的很全面&#xff0c;…...

游戏+AI的发展历程,AI技术在游戏行业的应用有哪些?

人工智能&#xff08;AI&#xff09;与游戏的结合&#xff0c;不仅是技术进步的体现&#xff0c;更是人类智慧的延伸。从最初的简单规则到如今的复杂决策系统&#xff0c;AI在游戏领域的发展历史可谓波澜壮阔。 早在2001年&#xff0c;就有研究指出游戏人工智能领域&#xff0…...

Methode Electronics EDI 需求分析

Methode Electronics 是一家总部位于美国的全球性技术公司&#xff0c;专注于设计和制造用于多个行业的电子和电气组件&#xff0c;产品涵盖汽车、工业、电信、医疗设备以及消费电子等多个领域&#xff0c;提供创新的解决方案。 填写Methode_EDI_Parameters_Template Methode_…...

2023AE软件、Adobe After Effects安装步骤分享教程

2023AE软件是一款由Adobe公司开发的视频编辑软件&#xff0c;也被称为Adobe After Effects。它在广告、电影、电视和网络视频等领域广泛应用&#xff0c;用于制作动态图形、特效、合成和其他视觉效果。该软件支持多种视频和音频文件格式&#xff0c;具有丰富的插件和预设&#…...

【前端】JavaScript 变量引用、内存与数组赋值:深入解析三种情景

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: 前端 文章目录 &#x1f4af;前言&#x1f4af;场景一&#xff1a;直接赋值与重新引用为什么结果不是 [3, 4, 5]&#xff1f;1. 引用与赋值的基本概念2. 图示分析 关键总结 &#x1f4af;场景二&#xff1a;引用指向的变化为什么…...

本地项目运行提示跨域问题

项目背景&#xff1a;我使用phpwebstudy在本地搭建了一个项目&#xff0c;然后前端是http://localhost:8080/ 后端我直接创建了一个本地域名&#xff0c;例如www.abc.com 然后vue.config.js配置如下&#xff0c;这个配置在我所有线上环境是没有任何问题的 devServer: {proxy…...

C++ —— string类(上)

目录 string的介绍 string类功能的使用介绍 constructor —— 构造 介绍使用&#xff08;1&#xff09;&#xff08;2&#xff09;&#xff08;4&#xff09; &#xff1a;构造、拷贝构造、带参构造 介绍&#xff08;3&#xff09;&#xff1a;拷贝string类对象的一部分字符…...

React Native Mac 环境搭建

下载 Mac 版Android Studio 下载 安装 JDK 环境 Flutter 项目实战-环境变量配置一 安装 Node.js 方式一 通过Node.js 官网下载 下载完成后点击安装包进行安装 安装完成...

Python Web 开发的路径管理艺术:FastAPI 项目中的最佳实践与问题解析20241119

Python Web 开发的路径管理艺术&#xff1a;FastAPI 项目中的最佳实践与问题解析 引言&#xff1a;从路径错误到模块化管理的技术旅程 在现代 Python Web 开发中&#xff0c;路径管理是一个常常被忽视却非常重要的问题。尤其是在使用像 FastAPI 和 Tortoise ORM 这样的框架时…...

Rust derive macro(Rust #[derive])Rust派生宏

参考文章&#xff1a;附录 D&#xff1a;派生特征 trait 文章目录 Rust 中的派生宏 #[derive]基础使用示例&#xff1a;派生 Debug 派生其他常用特征示例&#xff1a;派生 Clone 和 Copy 派生宏的限制和自定义派生自定义派生宏上面代码运行时报错了&#xff0c;以下是解释 结论…...

springboot嗨玩旅游网站

摘 要 嗨玩旅游网站是一个专为旅行爱好者打造的在线平台。我们提供丰富多样的旅游目的地信息&#xff0c;包括景点信息、旅游线路、商品信息、社区信息、活动推广等&#xff0c;帮助用户轻松规划行程。嗨玩旅游网站致力于为用户提供便捷、实用的旅行服务&#xff0c;让每一次旅…...

杰发科技AC7840——EEP中RAM的配置

sample和手册中示例代码的sram区地址定义不一样 这个在RAM中使用没有限制&#xff0c;根据这个表格留下足够空间即可 比如需要4096字节的eep空间&#xff0c;可以把RAM的地址改成E000&#xff0c;即E000-EFFF&#xff0c;共4096bytes即可。...

从零开始的c++之旅——map_set的使用

1.序列式容器和关联式容器 序列式容器&#xff1a;逻辑结构为线性序列的数据结构&#xff0c;两个位置之间没有紧密的关系&#xff0c;比如两者交换一下还是序列式的容器&#xff0c;例如string&#xff0c;vector&#xff0c;deque&#xff0c;array等。 关联式容器&#xff1…...

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 /…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手&#xff1a;借助大模型技术&#xff0c;开发能根据用户输入的主题、风格等要求&#xff0c;生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用&#xff0c;帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序

一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列&#xff0c;以便知晓哪些列包含有价值的数据&#xff0c;…...

Go 并发编程基础:通道(Channel)的使用

在 Go 中&#xff0c;Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式&#xff0c;用于在多个 Goroutine 之间传递数据&#xff0c;从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

MFC 抛体运动模拟:常见问题解决与界面美化

在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

stm32wle5 lpuart DMA数据不接收

配置波特率9600时&#xff0c;需要使用外部低速晶振...

AxureRP-Pro-Beta-Setup_114413.exe (6.0.0.2887)

Name&#xff1a;3ddown Serial&#xff1a;FiCGEezgdGoYILo8U/2MFyCWj0jZoJc/sziRRj2/ENvtEq7w1RH97k5MWctqVHA 注册用户名&#xff1a;Axure 序列号&#xff1a;8t3Yk/zu4cX601/seX6wBZgYRVj/lkC2PICCdO4sFKCCLx8mcCnccoylVb40lP...