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

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使用阶段&#xff0c;调用getBean()得到bean之后&#xff0c;根据需要&#xff0c;自行使用。 1 销毁Bean的几种方式 调用org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#destroyBean调用org.springframework.beans.factory.config.Conf…...

微信小程序可以进行dom操作吗?

小程序不能使用各种浏览器暴露出来的 DOM API&#xff0c;进行 DOM 选中和操作 原因&#xff1a;在小程序中&#xff0c;渲染层和逻辑层是分开的&#xff0c;分别运行在不同的线程中&#xff0c;逻辑层运行在 JSCore 中&#xff0c;并没有一个完整浏览器对象&#xff0c;因而缺…...

昇腾AI深耕沽上:港口辐射力之后,天津再添基础创新辐射力

作者 | 曾响铃 文 | 响铃说 AI计算正在以新基建联动产业集群的方式&#xff0c;加速落地。 不久前&#xff0c;天津市人工智能计算中心正式揭牌&#xff0c;该中心整体规划300P算力&#xff0c;2022年底首批100P算力上线投入运营&#xff0c;并实现上线即满载。 这是昇腾AI…...

基于YOLOv5的疲劳驾驶检测系统(Python+清新界面+数据集)

摘要&#xff1a;基于YOLOv5的疲劳驾驶检测系统使用深度学习技术检测常见驾驶图片、视频和实时视频中的疲劳行为&#xff0c;识别其闭眼、打哈欠等结果并记录和保存&#xff0c;以防止交通事故发生。本文详细介绍疲劳驾驶检测系统实现原理的同时&#xff0c;给出Python的实现代…...

【Linux】-- 进程优先级和环境变量

目录 进程的优先级 基本概念 如何查看优先级 PRI与NI NI值的设置范围 NI值如何修改 修改方式一 &#xff1a; 通过top指令修改优先级 修改方式二 &#xff1a; 通过renice指令修改优先级 进程的四个重要概念 环境变量 基本概念 常见的环境变量 查看环境变量 三种…...

iOS 紧急通知

一般通知 关于通知的各种配置和开发&#xff0c;可以参考推送通知教程&#xff1a;入门 – Kodeco&#xff0c;具有详细步骤。 紧急通知表现 紧急通知不受免打扰模式和静音模式约束。当紧急通知到达时&#xff0c;会有短暂提示音量和抖动&#xff08;约2s&#xff09;。未锁…...

即时零售:不可逆的进化

“人们经常问我&#xff0c;这个世界还是平的吗&#xff1f;我经常跟他们说&#xff0c;亲爱的&#xff0c;它真的是平的&#xff0c;比以前更平了。”2021年3月&#xff0c;《世界是平的》作者托马斯弗里德曼在演讲时说。如他所说&#xff0c;尽管逆全球化趋势加剧&#xff0c…...

零售数据总结经验:找好关键分析指标和维度

各位数据的朋友&#xff0c;大家好&#xff0c;我是老周道数据&#xff0c;和你一起&#xff0c;用常人思维数据分析&#xff0c;通过数据讲故事。 每逢月末、季末、年终&#xff0c;运营部门的同事又要开始进行年终总结分析。那么&#xff0c;对零售连锁企业来说&#xff0c;…...

从零开始搭建游戏服务器 第一节 创建一个简单的服务器架构

目录引言技术选型正文创建基础架构IDEA创建项目添加Netty监听端口编写客户端进行测试总结引言 由于现在java web太卷了&#xff0c;所以各位同行可以考虑换一个赛道&#xff0c;做游戏还是很开心的。 本篇教程给新人用于学习游戏服务器的基本知识&#xff0c;给新人们一些学习…...

C++中那些你不知道的未定义行为

引子 开篇我们先看一个非常有趣的引子&#xff1a; // 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索引的基本原理 索引是用来快速寻找特定的记录&#xff1b;把无序的数据变成有序的查询把创建索引的列数据进行排序对排序结果生成倒排表在倒排表的内容上拼接上地址链在查询时&#xff0c;先拿到倒排表内容&#xff0c;再取出地址链&#xff0c;最后拿到数据聚簇索引和非…...

@PropertySource使用场景

文章目录一、简单介绍二、注解说明1. 注解源码① PropertySource注解② PropertySources注解2. 注解使用场景3. 使用案例&#xff08;1&#xff09;新增test.properties文件&#xff08;2&#xff09;新增PropertySourceConfig类&#xff08;3&#xff09;新增PropertySourceTe…...

【C语言进阶:刨根究底字符串函数】strtok strerror函数

本节重点内容&#xff1a; 深入理解strtok函数的使用深入理解strerror函数的使用⚡strtok Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part ofstr1sep参数是个字符串&#xff0c;定义了用作分隔符的字符集合。第一个参数指…...

西安石油大学C语言期末重点知识点总结

大一学生一周十万字爆肝版C语言总结笔记 是我自己在学习完C语言的一次总结&#xff0c;尽管会有许多的瑕疵和不足&#xff0c;但也是自己对C语言的一次思考和探索&#xff0c;也让我开始有了写作博客的习惯和学习思考总结&#xff0c;争取等我将来变得更强的时候再去给它优化出…...

读《Multi-level Wavelet-CNN for Image Restoration》

Multi-level Wavelet-CNN for Image Restoration&#xff1a;MWCNN摘要一. 介绍二.相关工作三.方法摘要 存在的问题&#xff1a; 在低级视觉任务中&#xff0c;对于感受野尺寸与效率之间的平衡是一个关键的问题&#xff1b;普通卷积网络通常以牺牲计算成本去扩大感受野&#…...

【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,又一名功能测试摆脱点点点,进了大厂

咱们直接开门见山&#xff0c;没错我的粉丝向我投来了喜报&#xff0c;从功能测试转型测试开发&#xff0c;进入大厂&#xff0c;摆脱最初级的点点点功能测试&#xff0c;拿到高薪&#xff0c;遗憾的是&#xff0c;这名粉丝因为个人原因没有经过指导就去面试了&#xff0c;否则…...

数据结构之哈希表

常见的三种哈希结构 数组set&#xff08;集合&#xff09;map(映射) set&#xff08;集合&#xff09; 集合底层实现是否有序数值是否可以重复能否更改数值查询效率增删效率std::set红黑树有序否否O(log n)O(log n)std::multiset红黑树有序是否O(log n)O(log n)std::unordere…...

linux信号理解

linux信号&#xff1a;用户、系统或进程发送给目标进程的信息&#xff0c;以通知目标进程中某个状态的改变或是异常。 信号产生原因&#xff1a;软中断或者硬中断。可细分为如下几种原因&#xff1a; ①系统终端Terminal中输入特殊的字符来产生一个信号&#xff0c;比如按下&am…...

HC小区管理系统window系统安装教程

实操视频 HC小区管理系统局域网window物理机部署教程_哔哩哔哩_bilibili 一、下载安装包 百度网盘&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1XAjxtpeBjHIQUZs4M7TsRg 提取码&#xff1a;hchc 或者 123盘 hc-window.zip官方版下载丨最新版下载丨绿色版下…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

WebRTC从入门到实践 - 零基础教程

WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC&#xff1f; WebRTC&#xff08;Web Real-Time Communication&#xff09;是一个支持网页浏览器进行实时语音…...

git: early EOF

macOS报错&#xff1a; Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...

数据分析六部曲?

引言 上一章我们说到了数据分析六部曲&#xff0c;何谓六部曲呢&#xff1f; 其实啊&#xff0c;数据分析没那么难&#xff0c;只要掌握了下面这六个步骤&#xff0c;也就是数据分析六部曲&#xff0c;就算你是个啥都不懂的小白&#xff0c;也能慢慢上手做数据分析啦。 第一…...