【Spring专题】Spring之Bean生命周期源码解析——阶段四(Bean销毁)(拓展,了解就好)

news2024/11/17 13:40:31

目录

  • 前言
    • 阅读建议
  • 课程内容
    • 一、Bean什么时候销毁
    • 二、实现自定义的Bean销毁逻辑
      • 2.1 实现DisposableBean或者AutoCloseable接口
      • 2.2 使用@PreDestroy注解
      • 2.3 其他方式(手动指定销毁方法名字)
    • 三、注册销毁Bean过程及方法详解
      • 3.1 AbstractBeanFactory#requiresDestruction:需要销毁吗
      • 3.2 DisposableBeanAdapter.hasDestroyMethod:是否有销毁方法
      • 3.3 DisposableBeanAdapter#inferDestroyMethodIfNecessary:推断销毁方法
      • 3.4 AbstractBeanFactory#hasDestructionAwareBeanPostProcessors:是否有感知销毁Bean后置处理器
      • 3.5 DisposableBeanAdapter.hasApplicableProcessors:是否有应用于当前Bean的销毁感知Bean后置处理器
      • 3.6 DefaultSingletonBeanRegistry#registerDisposableBean:注册需要销毁的bean
      • 3.7 注册销毁Bean过程总结
    • 四、注册销毁Bean逻辑流程图
  • 学习总结

前言

我们在这里讲的是Bean的销毁过程。也许,不少朋友说到Bean的销毁,可能会想到垃圾回收的东西。虽然都是在做生命周期的最后一部分,但其实这俩不是同一回事。垃圾回收是JVM级别的东西,这里说的Bean销毁是Spring的东西,所以当然不是一回事。

阅读建议

本节课的内容,将会以下面这段代码为入口讲解:

		// 注册Bean的销毁接口
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;

这段代码,其实是在Spring实例化里面的AbstractAutowireCapableBeanFactory#doCreateBean()方法里面。而且,通过这个方法名字大家也知道了,这一步仅仅只是注册销毁逻辑而已,并不是真的销毁。只有当一定条件成立的时候,才会去销毁。
registerDisposableBeanIfNecessary具体代码如下:

  /**
     * 将给定bean添加到此工厂中的一次性bean列表中,注册其DisposableBean接口和/或给定的destroy方法,以便在工厂关闭时调用(如果适用)。只适用于单例。 
     * 参数: 
     * beanName—bean的名称—bean实例mbd—bean的bean定义 
     * 参见: 
     * RootBeanDefinition。isSingleton RootBeanDefinition。getDependsOn, registerDisposableBean, registerDependentBean
     */
    protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
        AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
        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));
            }
        }
    }

课程内容

一、Bean什么时候销毁

Bean销毁是发生在Spring容器关闭过程中。这时,Spring所有的单例Bean都会被销毁,并且,会执行各自实现了自定义销毁逻辑的Bean的销毁方法。我们在本篇文章要介绍的,就是:如何实现自定义的Bean销毁逻辑
在Spring容器关闭时,可以显示关闭,比如:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) context.getBean("userService");
userService.test();

// 容器关闭
context.close();

又或者,注册一个关闭钩子:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

// 注册关闭钩子
context.registerShutdownHook();

Object newUser = context.getBean("user");
System.out.println(newUser);

【注意:强制杀掉进程(kill pid)是不会调用自定义的Bean销毁逻辑】

Spring关闭容器的过程:

  1. 首先发布ContextClosedEvent事件
  2. 调用lifecycleProcessor的onCloese()方法
  3. 销毁单例Bean
    • 遍历disposableBeans
      • 把每个disposableBean从单例池中移除
      • 调用disposableBean的destroy()
      • 如果这个disposableBean还被其他Bean依赖了,那么也得销毁其他Bean
      • 如果这个disposableBean还包含了inner beans,将这些Bean从单例池中移除掉
    • 清空manualSingletonNames,是一个Set,存的是用户手动注册的单例Bean的beanName
    • 清空allBeanNamesByType,是一个Map,key是bean类型,value是该类型所有的beanName数组
    • 清空singletonBeanNamesByType,和allBeanNamesByType类似,只不过只存了单例Bean

二、实现自定义的Bean销毁逻辑

实现方式有如下几种:

2.1 实现DisposableBean或者AutoCloseable接口

需要自定义销毁的Bean代码示例:(实现自:DisposableBean )

@Component
public class TestDestroyBean implements DisposableBean {
    public void test() {
        System.out.println("测试一下销毁方法");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("TestDestroyBean------自定义的Bean销毁方法");
    }
}

或者:(实现自:AutoCloseable )

@Component
public class TestDestroyBean implements AutoCloseable {
    public void test() {
        System.out.println("测试一下销毁方法");
    }
    
    @Override
    public void close() throws Exception {
        System.out.println("TestDestroyBean------自定义的Bean销毁方法");
    }
}

2.2 使用@PreDestroy注解

实现方式有如下3种:

@Component
public class TestDestroyBean {
    public void test() {
        System.out.println("测试一下销毁方法");
    }

    @PreDestroy
    public void close() throws Exception {
        System.out.println("TestDestroyBean------自定义的Bean销毁方法");
    }
}

2.3 其他方式(手动指定销毁方法名字)

当然,还有其他方式,如:

<bean destroy-method='xxx'>

或者:

@Bean(destroyMethod = "xxx")

又或者就是,在beanDefinition里面直接指定销毁方法:

@Component
public class MyBeanPostProcessor implements MergedBeanDefinitionPostProcessor {
    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        if (beanName.equals("user")) {
            beanDefinition.setInitMethodName("myInit");
            beanDefinition.setDestroyMethodName("xxxx");
        }
    }
}

上面说的这三种方式,有一个比较特殊的地方,因为是手动指定的,所以可以设置一个比较特殊的值:(inferred)
如果设置了销毁方法名字为这个,并且Bean没有实现DisposableBean,则,在销毁的过程中,会检索bean下面有没有close或者shutdown方法。有,则自动绑定为【用户自定义销毁方法】。

三、注册销毁Bean过程及方法详解

本次销毁过程总过涉及了【3个核心类,6个核心方法】

3.1 AbstractBeanFactory#requiresDestruction:需要销毁吗

方法调用链:从入口:registerDisposableBeanIfNecessary()调用进来
全路径:org.springframework.beans.factory.support.AbstractBeanFactory#requiresDestruction
方法注释:将给定bean添加到此工厂中的一次性bean列表中,注册其DisposableBean接口和/或给定的destroy方法,以便在工厂关闭时调用(如果适用)。只适用于单例。

源码如下:

protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {
    return (
            bean.getClass() != NullBean.class && (
                    DisposableBeanAdapter.hasDestroyMethod(bean, mbd) 
                    || (hasDestructionAwareBeanPostProcessors() 
                            && DisposableBeanAdapter.hasApplicableProcessors(bean, getBeanPostProcessorCache().destructionAware))
            )
    );
}

方法解读:里面的关键源码其实就是分两个步骤。如下:

  1. 是否有指定的销毁方法。DisposableBeanAdapter.hasDestroyMethod(bean, mbd)
  2. 是否有DestructionAwareBeanPostProcessor,能感知销毁的Bean后置处理器(hasDestructionAwareBeanPostProcessors)。有则遍历DisposableBeanAdapter.hasApplicableProcessors(bean, getBeanPostProcessorCache().destructionAware)

3.2 DisposableBeanAdapter.hasDestroyMethod:是否有销毁方法

方法调用链:由3.1中的requiresDestruction()调用过来
全路径:org.springframework.beans.factory.support.DisposableBeanAdapter#hasDestroyMethod
方法注释:检查给定bean是否有任何要调用的销毁方法。

源码如下:

public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {
	return (bean instanceof DisposableBean || inferDestroyMethodIfNecessary(bean, beanDefinition) != null);
}

而里面的内容其实也很简单,步骤如下:

  1. 当前Bean是否实现了DisposableBean接口
  2. 没有,则调用inferDestroyMethodIfNecessary推断销毁方法(后面讲)

3.3 DisposableBeanAdapter#inferDestroyMethodIfNecessary:推断销毁方法

方法调用链:由3.2中的hasDestroyMethod()调用过来
全路径:org.springframework.beans.factory.support.DisposableBeanAdapter#inferDestroyMethodIfNecessary
方法注释:
如果给定beanDefinition的"destroyMethodName"属性的当前值是AbstractBeanDefinition。然后尝试推断一个销毁方法。候选方法目前仅限于名为“close”或“shutdown”的公共无参数方法(无论是在本地声明还是继承)。如果没有找到这样的方法,则将给定BeanDefinition的“destroyMethodName”更新为null,否则将设置为推断方法的名称。该常量作为@Bean#destroyMethod属性的默认值,该常量的值也可以在XML中或属性中使用。还处理java.io.Closeable和AutoCloseable接口,并在实现bean时反射地调用“close”方法。

源码如下:

	@Nullable
	private static String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
		String destroyMethodName = beanDefinition.resolvedDestroyMethodName;
		if (destroyMethodName == null) {
			destroyMethodName = beanDefinition.getDestroyMethodName();
			boolean autoCloseable = (bean instanceof AutoCloseable);
			if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||
					(destroyMethodName == null && autoCloseable)) {

				// 当销毁方法名字等于"(inferred)",且bean不是DisposableBean实现类
				destroyMethodName = null;
				if (!(bean instanceof DisposableBean)) {
					if (autoCloseable) {
						destroyMethodName = CLOSE_METHOD_NAME;
					}
					else {
						try {
							destroyMethodName = bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
						}
						catch (NoSuchMethodException ex) {
							try {
								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);
	}

方法解读:没啥好解读的了,直接重复贴一边注释就好了。尝试推断一个销毁方法。候选方法目前仅限于名为“close”或“shutdown”的公共无参数方法(无论是在本地声明还是继承)。如果没有找到这样的方法,则将给定BeanDefinition的“destroyMethodName”更新为null,否则将设置为推断方法的名称。该常量作为@Bean#destroyMethod属性的默认值,该常量的值也可以在XML中<bean destroy-method=“”>或属性中使用。还处理java.io.Closeable和AutoCloseable接口,并在实现bean时反射地调用“close”方法。

3.4 AbstractBeanFactory#hasDestructionAwareBeanPostProcessors:是否有感知销毁Bean后置处理器

方法调用链:由3.1中的requiresDestruction()调用过来
全路径:org.springframework.beans.factory.support.AbstractBeanFactory#hasDestructionAwareBeanPostProcessors
方法注释:返回该工厂是否持有一个DestructionAwareBeanPostProcessor,该DestructionAwareBeanPostProcessor将在关闭时应用于单例bean。

源码如下:

	protected boolean hasDestructionAwareBeanPostProcessors() {
		return !getBeanPostProcessorCache().destructionAware.isEmpty();
	}

3.5 DisposableBeanAdapter.hasApplicableProcessors:是否有应用于当前Bean的销毁感知Bean后置处理器

方法调用链:由3.1中的requiresDestruction()调用过来
全路径:org.springframework.beans.factory.support.DisposableBeanAdapter#hasApplicableProcessors
方法注释:检查给定bean是否有应用于它的销毁感知后处理器。

源码如下:

	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;
	}

经典的BeanPostProcessor处理了,不说了

3.6 DefaultSingletonBeanRegistry#registerDisposableBean:注册需要销毁的bean

方法调用链:从入口:registerDisposableBeanIfNecessary()调用进来
全路径:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#registerDisposableBean
方法注释:将给定的bean添加到此注册中心的销毁bean列表中。

源码如下:

private final Map<String, Object> disposableBeans = new LinkedHashMap<>();


public void registerDisposableBean(String beanName, DisposableBean bean) {
	synchronized (this.disposableBeans) {
		this.disposableBeans.put(beanName, bean);
	}
}

方法解读:所谓的注册,其实就是将当前bean及一些信息,添加到一个缓存map中。等到需要用到的时候,直接遍历map就好

3.7 注册销毁Bean过程总结

整体来说分为2个步骤:

  1. 是单例bean,判断是否需要销毁。判断步骤如下:(不同的Spring版本细节不一样,但是整体是一致的
    • 当前Bean是否实现了DisposableBean接口,是则直接返回true;否则进行【推断销毁方法】流程
    • 推断销毁方法
      • BeanDefinition中是否指定了destroyMethod,且destroyMethod==(inferred)。如果是,则寻找当前bean下是否有close方法或者shutdown方法,是则直接返回销毁方法名称
      • 或者当前Bean是否实现了AutoCloseable接口,是则直接返回销毁方法名称
    • 如果【推断销毁方法】也没有结果,则调用【感知销毁Bean后置处理器】DestructionAwareBeanPostProcessor.requiresDestruction(bean)进行判断
      • ApplicationListenerDetector中直接使得,如果当前bean是ApplicationListener子类需要销毁
      • InitDestroyAnnotationBeanPostProcessor中使得拥有@PreDestroy注解了的方法就是需要销毁
  2. 如果需要销毁,则适配成DisposableBeanAdapter对象,并存入disposableBeans中(一个LinkedHashMap)

四、注册销毁Bean逻辑流程图

在这里插入图片描述

学习总结

  1. 学习了Bean销毁过程
  2. 学习了注册销毁Bean的逻辑
  3. 学习了如何自定义Bean销毁逻辑

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/890090.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python列表笔记,python列表用法及基础操作

列表的介绍 定义100个变量&#xff0c;每个变量存放一个学生的姓名可行吗&#xff1f;有更好的办法吗&#xff1f; 答&#xff1a; 列表 一、列表的格式 定义列的格式&#xff1a;[元素1, 元素2, 元素3, ..., 元素n] 变量tmp的类型为列表 tmp [xiaoWang,180, 65.0] 列…

数模论文写作细节要求

目录 优秀论文必要条件 数学建模的基本思路 第一步&#xff1a;了解问题——查文献、找数据 第二步&#xff1a;阐述要解决什么问题、用什么方法 其余步骤&#xff1a;给出数学模型、计算求解、对比结果与真实情况、应用于现实问题。 使用某种数学方法的理由和依据 创…

【SAP-MDG】MDG配置-配置工作流任务 Configure Workflow Tasks

一、配置工作流任务的前提&#xff1a; 1.工作流能启动的常规设置已设置完毕&#xff1b;&#xff08;前面提到的内容&#xff09; 2.配置目录&#xff1a;SPRO→SAP NetWeaver→Application Server→Business Management→SAP Business Workflow.&#xff08;SPRO→SAP NetW…

Splashtop:适合旅行办公人士的远程工作解决方案

无论是重要的商务旅行&#xff0c;还是旅游度假&#xff0c;还是数字游牧生活方式的诱惑&#xff0c;许多人都会发现自己在机场贵宾室制作演示文稿&#xff0c;在古雅的咖啡馆参加虚拟会议&#xff0c;或者在偏远海滩的宁静背景下集思广益。 随处远程办公的生活让人向往。尽管…

5G科技防汛,助力守护一方平安

“立秋虽已至&#xff0c;炎夏尚还在”&#xff0c;受台风席卷以及季节性影响全国多地正面临强降水的严峻挑战。“落雨又顺秋&#xff0c;绵绵雨不休”&#xff0c;正值“七下八上” 防汛关键时期&#xff0c;贵州省水文水资源局已全面进入备战状态。 为确保及时响应做好防汛抢…

Linux SIGCHLD信号

在子进程结束之后&#xff0c;会默认给父进程发送SIGCHLD信号。 有以下代码&#xff1a; 编译并运行以上代码&#xff1a; 可以看到在子进程结束之后&#xff0c;将SIGCHLD信号发送给了父进程&#xff0c;然后输出SIGCHLD信号的代号17。 在后台运行以上代码并查看进程信息&am…

IDEAdebug调试时查看时间戳和Date类型的对象,格式化为yyyy-mm-hh

平时debug时遇到Date或者时间戳时看时间不好判断到底是几月几号&#xff0c;希望直接格式化为想要的格式 比如 下面&#xff0c;Date直接显示为 yyyy-mm-hh的格式 解决方案&#xff1a; 无需下载插件&#xff0c;如图 //Date格式化 return new SimpleDateFormat("…

SIP网络音频模块SV-2401V网络对讲音频模块(支持POE)

功能和特点 音频工作方式&#xff1a; 音频解码&#xff1a;即音频播放。接收来自网络的音频流&#xff0c;经过模块解码后通过线路输出高质量音频信号。目前支持可以播放以下音频格式&#xff1a;MP3、WAV (PCM IMA ADPCM)、G.711、G.722等&#xff0c;可以播放最高48k采样率…

汽车租赁管理系统/汽车租赁网站的设计与实现

摘 要 租赁汽车走进社区&#xff0c;走进生活&#xff0c;成为当今生活中不可缺少的一部分。随着汽车租赁业的发展&#xff0c;加强管理和规范管理司促进汽车租赁业健康发展的重要推动力。汽车租赁业为道路运输车辆一种新的融资服务形式、广大人民群众一种新的出行消费方式和…

每天一道leetcode:934. 最短的桥(图论中等广度优先遍历)

今日份题目&#xff1a; 给你一个大小为 n x n 的二元矩阵 grid &#xff0c;其中 1 表示陆地&#xff0c;0 表示水域。 岛 是由四面相连的 1 形成的一个最大组&#xff0c;即不会与非组内的任何其他 1 相连。grid 中 恰好存在两座岛 。 你可以将任意数量的 0 变为 1 &#…

全网最全的IntelliJ IDEA中快捷键大全,JetBrains公司IDE工具Windows快捷键大全,idea、pycharm快捷键大全

文章目录 前言零、工具的优点0.1快速修复0.2代码自动补全0.3实时模板0.4显示历史版本0.5调试功能 一、Ctrl二、Alt三、Shift四、Ctrl Alt五、Ctrl Shift六、Alt Shift七、Ctrl Shift Alt八、其它 前言 工欲善其事必先利其器&#xff0c;JetBrains公司IDE工具为我们的开发…

用友U8+CRM 任意文件上传+读取漏洞复现

0x01 产品简介 用友U8 CRM客户关系管理系统是一款专业的企业级CRM软件&#xff0c;旨在帮助企业高效管理客户关系、提升销售业绩和提供优质的客户服务。 0x02 漏洞概述 用友 U8 CRM客户关系管理系统 getemaildata.php 文件存在任意文件上传和任意文件读取漏洞&#xff0c;攻击…

7zip分卷压缩

前言 有些项目上传文件大小有限制 压缩包大了之后传输也会比较慢 解决方案 我们可以利用7zip压缩工具对文件进行分卷压缩 利用7zip压缩工具进行分卷压缩 查看待压缩文件大小 压缩完成之后有300多M&#xff0c;我们用100M去进行分卷压缩 选择待压缩的文件夹&#xff0c;右…

Python几何计算库读取几何,点线面要素游标搜索

Python几何计算库读取几何&#xff0c;点线面要素游标搜索 一、读取几何 要素类中的每个要素都包含一组用于定义面或线折点的点要素&#xff0c;或者包含单个用于定义一个点要素的坐标。可以使用几何对象&#xff08; 面 Polygon、 折线 Polyline、 点几何 PointGeometry 或 多…

性能比较 - Spring Boot 应用程序中的线程池与虚拟线程 (Project Loom)

本文比较了 Spring Boot 应用程序中的不同请求处理方法&#xff1a;ThreadPool、WebFlux、协程和虚拟线程 (Project Loom)。 在本文中&#xff0c;我们将简要描述并粗略比较可在 Spring Boot 应用程序中使用的各种请求处理方法的性能。 高效的请求处理在开发高性能后端…

Datawhale Django后端开发入门 Vscode TASK02 Admin管理员、外键的使用

一.Admin管理员的使用 1、启动django服务 使用创建管理员之前&#xff0c;一定要先启动django服务&#xff0c;虽然TASK01和TASK02是分开的&#xff0c;但是进行第二个流程的时候记得先启动django服务&#xff0c;注意此时是在你的项目文件夹下启动的&#xff0c;时刻注意要执…

安装Google cloud时出现错误如何解决

在安装Google cloud出现错误 找到这个路径下的文件&#xff0c;并将其删除。 C:\\Users\\Administrator\\AppData\\Roaming\\gcloud\\configurations\\config_default删除后&#xff0c;重新运行安装程序&#xff0c;问题解决&#xff01;&#xff01;&#xff01;

PyTorch训练简单的生成对抗网络GAN

文章目录 原理代码结果参考 原理 同时训练两个网络&#xff1a;辨别器Discriminator 和 生成器Generator Generator是 造假者&#xff0c;用来生成假数据。 Discriminator 是警察&#xff0c;尽可能的分辨出来哪些是造假的&#xff0c;哪些是真实的数据。 目的&#xff1a;使…

FreeRTOS qemu mps2-an385 bsp 移植制作 :系统运行篇

相关文章 FreeRTOS qemu mps2-an385 bsp 移植制作 &#xff1a;环境搭建篇 FreeRTOS qemu mps2-an385 bsp 移植制作 &#xff1a;系统启动篇 开发环境 Win10 64位 VS Code&#xff0c;ssh 远程连接 ubuntu VMware Workstation Pro 16 Ubuntu 20.04 FreeRTOSv202212.01&a…

DataFrame.rename()函数--Pandas

1. 函数作用 修改DataFrame的行名、列名 2. 函数语法 DataFrame.rename(mapperNone, *, indexNone, columnsNone, axisNone, copyNone, inplaceFalse, levelNone, errorsignore)3. 函数参数 参数含义mapper与axis结合使用&#xff0c;表示运用到axis上的值&#xff1a;类字…