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官方版下载丨最新版下载丨绿色版下…...
XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
