0108Bean销毁-Bean生命周期详解-spring
Bean使用阶段,调用getBean()得到bean之后,根据需要,自行使用。
1 销毁Bean的几种方式
- 调用org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#destroyBean
- 调用org.springframework.beans.factory.config.ConfigurableBeanFactory#destroySingletons
- 调用ApplicationContext中的close方法,实际会调用起BeanFactory的destroySingletons方法
第一种为销毁单个bean,后两种为销毁容器中所有的单例beans。我们下面主要讲解第一种方式,后面等到讲解容器的时候在详细介绍。
2 Bean销毁逻辑
- 轮询beanPostProcessors列表,如果是DestructionAwareBeanPostProcessor这种类型的,会调用其内部的postProcessBeforeDestruction方法
- 如果bean实现了org.springframework.beans.factory.DisposableBean接口,会调用这个接口中的destroy方法
- 如果bean实现了java.lang.AutoCloseable接口,调用该接口中的close()方法,这个下面不详细讲解。
- 调用bean自定义的销毁方法
追踪下AbstractAutowireCapableBeanFactory#destroyBean方法,代码如下:
@Override
public void destroyBean(Object existingBean) {new DisposableBeanAdapter(existingBean, getBeanPostProcessorCache().destructionAware).destroy();
}
会继续调用DisposableBeanAdapter的destroy方法,代码如下:
@Overridepublic void destroy() {if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {processor.postProcessBeforeDestruction(this.bean, this.beanName);}}if (this.invokeDisposableBean) {// 省略日志记录try {((DisposableBean) this.bean).destroy();}// 省略异常处理}if (this.invokeAutoCloseable) {// 省略日志记录try {((AutoCloseable) this.bean).close();}// 省略异常处理}else if (this.destroyMethods != null) {for (Method destroyMethod : this.destroyMethods) {invokeCustomDestroyMethod(destroyMethod);}}else if (this.destroyMethodNames != null) {for (String destroyMethodName: this.destroyMethodNames) {Method destroyMethod = determineDestroyMethod(destroyMethodName);if (destroyMethod != null) {invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(destroyMethod, this.bean.getClass()));}}}}
3 DestructionAwareBeanPostProcessor后置处理器
package org.springframework.beans.factory.config;import org.springframework.beans.BeansException;public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {/*** 销毁之前执行*/void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;/*** 是否要执行销毁*/default boolean requiresDestruction(Object bean) {return true;}}
3.1 CommonAnnotationBeanPostProcessor
其中这个接口有个重要的实现类
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
这个类中并没有postProcessBeforeDestruction方法,即它直接继承自起父类InitDestroyAnnotationBeanPostProcessor,代码如下:
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());try {metadata.invokeDestroyMethods(bean, beanName);}// 省略。。。
}
public void invokeDestroyMethods(Object target, String beanName) throws Throwable {Collection<LifecycleElement> checkedDestroyMethods = this.checkedDestroyMethods;Collection<LifecycleElement> destroyMethodsToUse =(checkedDestroyMethods != null ? checkedDestroyMethods : this.destroyMethods);if (!destroyMethodsToUse.isEmpty()) {for (LifecycleElement element : destroyMethodsToUse) {if (logger.isTraceEnabled()) {logger.trace("Invoking destroy method on bean '" + beanName + "': " + element.getMethod());}element.invoke(target);}}
}
那么在detroMethods中有哪些销毁方法呢?我们看下上面findLifecycleMetadata方法,代码如下;
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;
}
- 判断lifecycleMetadataCache缓存为空
- 为空,执行buildLifecycleMetadata方法
- 不为空,直接缓存获取
我们看下缓存为空的情况,即执行buildLifecycleMetadata方法,
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 -> {if (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);}}if (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));
}
该方法用于收集bean的初始化方法和销毁方法:
-
遍历目标bean Class的方法,判断方法上注解是否是initAnnotationType类型,如果是加入初始化方法集合;如果方法注解是destroyAnnotationType类型,加入销毁方法集合。
-
initAnnotationType和destroyAnnotationType类型,是在CommonAnnotationBeanPostProcessor类的构造方法里面设置的
public CommonAnnotationBeanPostProcessor() {setOrder(Ordered.LOWEST_PRECEDENCE - 3);setInitAnnotationType(PostConstruct.class);setDestroyAnnotationType(PreDestroy.class);// java.naming module present on JDK 9+?if (jndiPresent) {this.jndiFactory = new SimpleJndiBeanFactory();}} -
即destroyAnnotationType类型就是判断方法注解是否是@PreDestroy
CommonAnnotationBeanPostProcessor#postProcessBeforeDestruction方法中会调用bean中所有标注了@PreDestroy的方法
4 @PreDestroy注解
@PreDestroy注解主要用于在对象销毁前执行一些清理工作,比如释放资源或者关闭连接。
下面来做个测试,测试代码如下:
package com.gaogzhen.myspring.bean;import jakarta.annotation.PreDestroy;/*** 测试@PreDestory* @author gaogzhen*/
public class DestroyBean {public DestroyBean() {System.out.println("creat======");}@PreDestroypublic void testPreDestroy() {System.out.println("===preDestroy===");}
}
@Test
public void testPreDestroy() {DefaultListableBeanFactory factory = new DefaultListableBeanFactory();// 自定义DestructionAwareBeanPostProcessorfactory.addBeanPostProcessor((DestructionAwareBeanPostProcessor) (bean, beanName) -> System.out.println("准备销毁bean:" + beanName));// 默认CommonAnnotationBeanPostProcessorfactory.addBeanPostProcessor(new CommonAnnotationBeanPostProcessor());// 容器注入BeanDefinitionfactory.registerBeanDefinition("destroyBean", BeanDefinitionBuilder.genericBeanDefinition(DestroyBean.class).getBeanDefinition());// 触发所有单例bean初始化factory.preInstantiateSingletons();// 销毁指定beanfactory.destroySingleton("destroyBean");
}
测试结果:
creat======
准备销毁bean:destroyBean
===preDestroy===
5 DisposableBean接口
public interface DisposableBean {/*** 在bean被销毁时被调用*/void destroy() throws Exception;}
- 作用用于销毁前释放资源或者连接
这里不单独做测试,在下面#6一起测试下执行顺序。
6 自定义销毁方法
我们将通过AnnotationConfigApplicationContext来演示下,当bean被销毁时,@PreDestroy,DisposableBean以及自定义销毁方法的执行顺序。
示例代码如下6-1所示:
package com.gaogzhen.myspring.service;import jakarta.annotation.PreDestroy;
import org.springframework.beans.factory.DisposableBean;/*** 测试销毁相关的方法执行顺序* @author gaogzhen*/
public class ServiceDestroy implements DisposableBean {@Overridepublic void destroy() throws Exception {System.out.println("DisposableBean接口方法执行");}@PreDestroypublic void preDestroy() {System.out.println("@PreDestroy注解方法执行");}public void customDestroy() {System.out.println("自定义销毁方法执行");}
}public class TestDestroy {@Bean(destroyMethod = "customDestroy")public ServiceDestroy serviceDestroy() {return new ServiceDestroy();}@Testpublic void testDestroyOrder() {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.register(TestDestroy.class);System.out.println("准备启动容器");context.refresh();System.out.println("容器启动完毕");System.out.println("serviceDestroy: " + context.getBean(ServiceDestroy.class));System.out.println("准备关闭容器");context.close();System.out.println("容器关闭");}
}
测试结果:
准备启动容器
容器启动完毕
serviceDestroy: com.gaogzhen.myspring.service.ServiceDestroy@40e10ff8
准备关闭容器
@PreDestroy注解方法执行
DisposableBean接口方法执行
容器关闭
后记
如果小伙伴什么问题或者指教,欢迎交流。
❓QQ:806797785
⭐️源代码仓库地址:https://gitee.com/gaogzhen/spring6-study
参考:
[1]Spring系列之Bean生命周期详解[CP/OL]
相关文章:
0108Bean销毁-Bean生命周期详解-spring
Bean使用阶段,调用getBean()得到bean之后,根据需要,自行使用。 1 销毁Bean的几种方式 调用org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#destroyBean调用org.springframework.beans.factory.config.Conf…...
微信小程序可以进行dom操作吗?
小程序不能使用各种浏览器暴露出来的 DOM API,进行 DOM 选中和操作 原因:在小程序中,渲染层和逻辑层是分开的,分别运行在不同的线程中,逻辑层运行在 JSCore 中,并没有一个完整浏览器对象,因而缺…...
昇腾AI深耕沽上:港口辐射力之后,天津再添基础创新辐射力
作者 | 曾响铃 文 | 响铃说 AI计算正在以新基建联动产业集群的方式,加速落地。 不久前,天津市人工智能计算中心正式揭牌,该中心整体规划300P算力,2022年底首批100P算力上线投入运营,并实现上线即满载。 这是昇腾AI…...
基于YOLOv5的疲劳驾驶检测系统(Python+清新界面+数据集)
摘要:基于YOLOv5的疲劳驾驶检测系统使用深度学习技术检测常见驾驶图片、视频和实时视频中的疲劳行为,识别其闭眼、打哈欠等结果并记录和保存,以防止交通事故发生。本文详细介绍疲劳驾驶检测系统实现原理的同时,给出Python的实现代…...
【Linux】-- 进程优先级和环境变量
目录 进程的优先级 基本概念 如何查看优先级 PRI与NI NI值的设置范围 NI值如何修改 修改方式一 : 通过top指令修改优先级 修改方式二 : 通过renice指令修改优先级 进程的四个重要概念 环境变量 基本概念 常见的环境变量 查看环境变量 三种…...
iOS 紧急通知
一般通知 关于通知的各种配置和开发,可以参考推送通知教程:入门 – Kodeco,具有详细步骤。 紧急通知表现 紧急通知不受免打扰模式和静音模式约束。当紧急通知到达时,会有短暂提示音量和抖动(约2s)。未锁…...
即时零售:不可逆的进化
“人们经常问我,这个世界还是平的吗?我经常跟他们说,亲爱的,它真的是平的,比以前更平了。”2021年3月,《世界是平的》作者托马斯弗里德曼在演讲时说。如他所说,尽管逆全球化趋势加剧,…...
零售数据总结经验:找好关键分析指标和维度
各位数据的朋友,大家好,我是老周道数据,和你一起,用常人思维数据分析,通过数据讲故事。 每逢月末、季末、年终,运营部门的同事又要开始进行年终总结分析。那么,对零售连锁企业来说,…...
从零开始搭建游戏服务器 第一节 创建一个简单的服务器架构
目录引言技术选型正文创建基础架构IDEA创建项目添加Netty监听端口编写客户端进行测试总结引言 由于现在java web太卷了,所以各位同行可以考虑换一个赛道,做游戏还是很开心的。 本篇教程给新人用于学习游戏服务器的基本知识,给新人们一些学习…...
C++中那些你不知道的未定义行为
引子 开篇我们先看一个非常有趣的引子: // test.cpp int f(long *a, int *b) {*b 5;*a 1;return *b; }int main() {int x 10;int *p &x;auto q (long *)&x;auto ret f(q, p);std::cout << x << std::endl;std::cout << ret <&…...
java基础面试题(四)
Mysql索引的基本原理 索引是用来快速寻找特定的记录;把无序的数据变成有序的查询把创建索引的列数据进行排序对排序结果生成倒排表在倒排表的内容上拼接上地址链在查询时,先拿到倒排表内容,再取出地址链,最后拿到数据聚簇索引和非…...
@PropertySource使用场景
文章目录一、简单介绍二、注解说明1. 注解源码① PropertySource注解② PropertySources注解2. 注解使用场景3. 使用案例(1)新增test.properties文件(2)新增PropertySourceConfig类(3)新增PropertySourceTe…...
【C语言进阶:刨根究底字符串函数】strtok strerror函数
本节重点内容: 深入理解strtok函数的使用深入理解strerror函数的使用⚡strtok Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part ofstr1sep参数是个字符串,定义了用作分隔符的字符集合。第一个参数指…...
西安石油大学C语言期末重点知识点总结
大一学生一周十万字爆肝版C语言总结笔记 是我自己在学习完C语言的一次总结,尽管会有许多的瑕疵和不足,但也是自己对C语言的一次思考和探索,也让我开始有了写作博客的习惯和学习思考总结,争取等我将来变得更强的时候再去给它优化出…...
读《Multi-level Wavelet-CNN for Image Restoration》
Multi-level Wavelet-CNN for Image Restoration:MWCNN摘要一. 介绍二.相关工作三.方法摘要 存在的问题: 在低级视觉任务中,对于感受野尺寸与效率之间的平衡是一个关键的问题;普通卷积网络通常以牺牲计算成本去扩大感受野&#…...
【Linux】安装DHCP服务器
1、先检测网络是否通 get dhcp.txt rpm -qa //查看软件包 rpm -qa |grep dhcp //确定是否安装 yum install dhcp //进行安装 安装完成后 查询 rpm -ql dhcp 进行配置 cd /etc/dhcp 查看是否有遗留dhcpd.conf.rpmsave 删除该文件 cp /usr/share/doc/dhcp-4.1.1/dhcpd.conf.sampl…...
功能测试转型测试开发年薪27W,又一名功能测试摆脱点点点,进了大厂
咱们直接开门见山,没错我的粉丝向我投来了喜报,从功能测试转型测试开发,进入大厂,摆脱最初级的点点点功能测试,拿到高薪,遗憾的是,这名粉丝因为个人原因没有经过指导就去面试了,否则…...
数据结构之哈希表
常见的三种哈希结构 数组set(集合)map(映射) set(集合) 集合底层实现是否有序数值是否可以重复能否更改数值查询效率增删效率std::set红黑树有序否否O(log n)O(log n)std::multiset红黑树有序是否O(log n)O(log n)std::unordere…...
linux信号理解
linux信号:用户、系统或进程发送给目标进程的信息,以通知目标进程中某个状态的改变或是异常。 信号产生原因:软中断或者硬中断。可细分为如下几种原因: ①系统终端Terminal中输入特殊的字符来产生一个信号,比如按下&am…...
HC小区管理系统window系统安装教程
实操视频 HC小区管理系统局域网window物理机部署教程_哔哩哔哩_bilibili 一、下载安装包 百度网盘: 链接:https://pan.baidu.com/s/1XAjxtpeBjHIQUZs4M7TsRg 提取码:hchc 或者 123盘 hc-window.zip官方版下载丨最新版下载丨绿色版下…...
AI智能体视觉(TVA)实战教程(1)
重磅预告:本专栏将独家连载新书《AI视觉技术:从入门到进阶》精华内容。本书是《AI视觉技术:从进阶到专家》的权威前导篇,特邀美国 TypeOne 公司首席科学家、斯坦福大学博士 Bohan 担任技术顾问。Bohan先生师从美国三院院士、“AI教…...
FGO自动化终极指南:告别枯燥刷本,每天节省3小时游戏时间
FGO自动化终极指南:告别枯燥刷本,每天节省3小时游戏时间 【免费下载链接】FGA Auto-battle app for F/GO Android 项目地址: https://gitcode.com/gh_mirrors/fg/FGA 你是否厌倦了在《Fate/Grand Order》(FGO)中重复点击刷…...
基于h2oGPT构建本地私有化知识库:从RAG原理到实战部署
1. 项目概述:一个真正私密的本地文档智能助手 如果你和我一样,对把敏感的工作文档、个人笔记或者内部资料上传到云端总有些提心吊胆,但又眼馋ChatGPT那种强大的文档理解和对话能力,那么h2oGPT的出现,可以说是解了我们…...
从CEO到营销技术专家:创业者退休后的身份重构与价值延续
1. 从创业者到“退休者”:身份的骤然转变卖掉自己一手创办并经营了近四十年的公司,这种感觉,远非“退休”二字可以概括。它不是一次计划已久的悠闲旅行,更像是一场毫无预兆的急刹车。前一天,你还在会议室里为下一代产品…...
2026年,性价比超高的直播代运营供应商究竟哪家强?
在直播电商行业持续火爆的当下,众多品牌都希望借助直播代运营服务来提升销售业绩和品牌影响力。然而,市场上直播代运营供应商众多,质量参差不齐,如何选择一家性价比超高的供应商成为了品牌方的一大难题。今天,就为大家…...
Let‘s Encrypt证书有效期缩短至90天后,如何实现自动续期
Let’s Encrypt证书有效期缩短至90天后,如何实现自动续期 打开网站突然发现浏览器地址栏一把红色小锁,提示"您的连接不是专用连接"——SSL证书过期了。这可能是站长最不想看到的画面之一:用户无法正常访问、搜索引擎排名下降、甚至…...
Claude Code集成X API:一键发推提升开发者分享效率
1. 项目概述:在 Claude Code 中无缝发布 X 推文如果你和我一样,日常开发工作流已经深度整合了 Claude Code,那么你肯定体会过那种“心流”被打断的瞬间:当你在终端里调试出一个漂亮的解决方案,或者用脚本跑出了一个惊艳…...
双模型工作流架构解析:从原理到实践,构建高效AI应用
1. 项目概述:双模型工作流的魅力与挑战最近在GitHub上看到一个挺有意思的项目,叫cait52099/openclaw-dual-model-workflow。光看名字,openclaw(开放之爪)和dual-model-workflow(双模型工作流)这…...
AI网关架构解析:统一管理多模型API,提升服务治理与性能
1. 项目概述:一个AI驱动的开源网关框架最近在开源社区里,我注意到一个名为hoazgazh/aigate的项目。这个名字乍一看有点神秘,但拆解一下,“aigate”直译就是“AI网关”。这立刻让我联想到当前技术领域的一个核心痛点:如…...
Scroll Reverser终极指南:5分钟解决macOS多设备滚动方向混乱问题
Scroll Reverser终极指南:5分钟解决macOS多设备滚动方向混乱问题 【免费下载链接】Scroll-Reverser Per-device scrolling prefs on macOS. 项目地址: https://gitcode.com/gh_mirrors/sc/Scroll-Reverser 你是否经常在MacBook触控板和鼠标之间切换ÿ…...
