当前位置: 首页 > 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官方版下载丨最新版下载丨绿色版下…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

视频字幕质量评估的大规模细粒度基准

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用&#xff0c;因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型&#xff08;VLMs&#xff09;在字幕生成方面…...

【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验

系列回顾&#xff1a; 在上一篇中&#xff0c;我们成功地为应用集成了数据库&#xff0c;并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了&#xff01;但是&#xff0c;如果你仔细审视那些 API&#xff0c;会发现它们还很“粗糙”&#xff1a;有…...

多模态大语言模型arxiv论文略读(108)

CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题&#xff1a;CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者&#xff1a;Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...

laravel8+vue3.0+element-plus搭建方法

创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...

HDFS分布式存储 zookeeper

hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架&#xff0c;允许使用简单的变成模型跨计算机对大型集群进行分布式处理&#xff08;1.海量的数据存储 2.海量数据的计算&#xff09;Hadoop核心组件 hdfs&#xff08;分布式文件存储系统&#xff09;&a…...

回溯算法学习

一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...