【Spring源码系列】Bean生命周期-Bean销毁

news2025/2/24 6:01:22

前言

Spring给我们提供了一种当bean销毁时调用某个方法的方式。那么,Spring底层到底是如何实现的呢?接下来,我们将从源码+案例的方式来解析:spring如何实现当bean销毁时调用某个方法的。

一、Bean销毁介绍

bean销毁的时机

spring容器关闭的时候(调用close())方法的时候,那么对于实现destroy方法的bean,就会开始执行各自自定义的销毁逻辑。

提示:
是spring容器关闭的时候调用bean销毁逻辑,不是垃圾回收、程序意外终止、程序正常终止…的时候。

spring注册DestroyBean时机

1、注册DisposableBeans。在‘初始化后’会对BeanDefinition进行判断,判断该BeanDefinition是否具备destroy方法,如果具备则把BeanDefinition注册到DisposableBeans。具体如何判断的,我们下面会讲;
在这里插入图片描述
2、执行destroy方法。当调用close方法的时候,会遍历DisposableBeans执行每一个销毁方法

常用的定义bean销毁方式

此处不仅仅写了代码示例,也把源码贴出来进行验证。

使用@PreDestroy注解

代码示例:

@Component
public class UserService {

	@Autowired
	private OrderService orderService;

	public void test(){
		System.out.println(orderService);
	}

	@PreDestroy
	public void myDestroyUserServiceMethod () {
		System.out.println("UserService#myDestroyUserServiceMethod");
	}
}

源码:

	protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {
		// hasDestroyMethod: 实现了DisposableBean或者AutoCloseable接口	,或者创建bean的时候手动指定了销毁方法( 比如@Bean(destroyMethod = "destory")、xml中的bean标签中指定destroyMethod)
		return (bean.getClass() != NullBean.class && (DisposableBeanAdapter.hasDestroyMethod(bean, mbd) ||
				// @PreDestroy注解
				(hasDestructionAwareBeanPostProcessors() && DisposableBeanAdapter.hasApplicableProcessors(
						bean, getBeanPostProcessorCache().destructionAware))));
	}

代码调试:
CommonAnnotationBeanPostProcessor:
在这里插入图片描述UserService#myDestroyUserServiceMethod销毁方法在Spring容器启动的时候就已经被记录在CommonAnnotationBeanPostProcessor中了,当调用org.springframework.beans.factory.support.AbstractBeanFactory#requiresDestruction判断该bean是否定义销毁逻辑的时候返回的是true:
在这里插入图片描述

实现DisposableBean或者AutoCloseable接口

代码示例:

@Component
public class UserService implements DisposableBean {

	@Autowired
	private OrderService orderService;

	public void test(){
		System.out.println(orderService);
	}

	@Override
	public void destroy () {
		System.out.println("UserService#destroy");
	}
}

源码:

	public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {
		// 是否实现了这两个接口中的一个
		if (bean instanceof DisposableBean || bean instanceof AutoCloseable) {
			return true;
		}
		// 判断BeanDefinition是否指定了销毁方法
		return inferDestroyMethodIfNecessary(bean, beanDefinition) != null;
	}

源码调试:
UserService 实现了 DisposableBean 接口,所以DisposableBeanAdapter.hasDestroyMethod(bean, mbd)返回true,且可以发现CommonAnnotationBeanPostProcessor#lifecycleMetadataCache集合中的UserService.class并没指定destroyMethods:
在这里插入图片描述

手动指定destroy方法(@Bean、XML)

手动指定dstroy方法有两种方式:
1、@Bean注解方式指定destroyMethod;
2、XML文件中< bean >标签里面指定destry-method;

public class OrderService {

	public void destroy () {
		System.out.println("OrderService#destroy");
	}
}
@ComponentScan("com.cms")
public class AppConfig {

	@Bean(destroyMethod = "destroy")
	public OrderService createOrderService () {
		return new OrderService();
	}
	
}

在这里插入图片描述

手动指定destroy方法((inferred))

代码示例:

public class OrderService {

  // 必须是close方法
	public void close () {
		System.out.println("OrderService#destroy");
	}
}
@ComponentScan("com.cms")
public class AppConfig {

	@Bean(destroyMethod = "(inferred)")
	public OrderService createOrderService () {
		return new OrderService();
	}
	
}

源码:

	@Nullable
	private static String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
		// 判断BeanDefinition是否指定了销毁方法(比如创建bean的时候(@Bean、xml),手动指定destroyMethod)
		String destroyMethodName = beanDefinition.resolvedDestroyMethodName;
		// 下面这种定义销毁的方式,不常用。流程:先定义销毁方法-(inferred) ,然后调用close方法。
		if (destroyMethodName == null) {
			destroyMethodName = beanDefinition.getDestroyMethodName(); //
			if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||
					(destroyMethodName == null && bean instanceof AutoCloseable)) {
				// Only perform destroy method inference or Closeable detection
				// in case of the bean not explicitly implementing DisposableBean
				destroyMethodName = null;
				if (!(bean instanceof DisposableBean)) {
					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);
	}

手动指定destroy方法(MergedBeanDefinitionPostProcessor后置处理器设置销毁方法)

@Component
public class MyMergeBdfPostProcesser implements MergedBeanDefinitionPostProcessor {
	@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		if (beanName.equals("myDisposableBean3")){
			beanDefinition.setDestroyMethodName("a");
		}
	}
}
 
 
@Component
public class MyDisposableBean3 {
 
	public void a()  {
		System.out.println("MyMergeBdfPostProcesser-后置处理器销毁");
	}
}

或者

@Component
public class MyMergeBdfPostProcesser2 implements MergedBeanDefinitionPostProcessor {
	@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		if (beanName.equals("myDisposableBean4")){
			beanDefinition.setDestroyMethodName("(inferred)");
		}
	}
}
 
 
 
@Component
public class MyDisposableBean4 {
 
//	public void close()  {
//		System.out.println("close销毁");
//	}
 
 
	public void shutdown()  {
		System.out.println("shutdown销毁");
	}
}

二、Bean销毁-源码分析

声明关键点

1、原型bean即使定义了销毁方法,但是执行不会调用销毁方法。

源代码

源码位置:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

// 只做一件事情:注册实现了'销毁'方法的bean。
registerDisposableBeanIfNecessary(beanName, bean, mbd);
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
		AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
		// if(不是'多例'bean && 有销毁方法)
		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));
			}
		}
	}

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

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

相关文章

[附源码]计算机毕业设计springboot智能衣橱APP

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

数据增强 - 当数据有限时如何使用深度学习(一)

本文是对图像的深度学习数据增强技术的全面综述前言我们都遇到过这种情况&#xff1a;您有一个可以使用机器学习模型实现的机会&#xff0c;但当您打开网络浏览器并搜索相关数据&#xff0c;很有可能&#xff0c;您会发现一个只有几百张图像的数据集。您记得最受欢迎的数据集的…

【项目_03】日历的回显、搭建热门精选、下拉加载更多、搜索框搭建 | 基于Vue3全家桶

&#x1f4ad;&#x1f4ad; ✨: 日历的回显、搭建热门精选、下拉加载更多、搜索框搭建 | 旅途拾景   &#x1f49f;&#xff1a;东非不开森的主页   &#x1f49c;: 心若有所向往&#xff0c;何惧道阻且长&#x1f49c;&#x1f49c;   &#x1f338;: 如有错误或不足之处&…

Android入门第38天-使用随鼠标移动的圆点来熟悉onTouchEvent

简介 平时包括之前的例子大量是基于TouchListener如&#xff1a;onClick这种一类的事件。 今天给大家带来的是TouchListener与OnTouchEvent的比较&#xff0c;以及多点触碰的知识点&#xff01; TouchListener是基于监听的&#xff0c;而OnTouchEvent则是基于回调的&#xff…

SNMP协议——网络管理概述

作者简介&#xff1a;一名在校云计算网络运维学生、每天分享网络运维的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 一.网络管理概述 1.网络管理概念 2.网络管理功能 1.配置管理 …

Xshell连接如何记住用户秘钥文件

场景描述 服务器ssh登录有2种常用的方式&#xff1a; 方式1&#xff1a;用户密码认证&#xff0c;登录时输入用户名和密码。方式2&#xff1a;公钥认证(Public Key User Authentication)&#xff0c;登录时提供用户名私钥。公钥需要存储一份在服务器上该用户的~/.ssh/authori…

是德科技DSOX2002A型号示波器的使用

文章目录图使用自检电路单次触发按键恢复出厂设置水平调整区水平位移调整按钮水平时机调整旋钮垂直调整区通道启用按钮垂直档位调节旋钮垂直位移调整旋钮测量区图 使用 自检 黑色鳄鱼夹接地&#xff0c;伸缩探钩挂到左侧。 按下auto按键 使用测量按键可以调出波形的峰峰值与频…

Redis (持续更新…)

提到这个词&#xff0c;脑海里一连串&#xff1a;它是什么&#xff0c;能干什么&#xff0c;怎么干的&#xff0c;不用它行不行&#xff0c;有没有同类…… 其实就是5W2H&#xff0c;或者说凡事多问个为什么 先放官网&#xff1a; ​​​​​​Redis CRUG网站 它是开源存储…

非零基础自学Golang 2 开发环境 2.5 第一个Go 程序

非零基础自学Golang 学习文档地址&#xff1a;https://www.topgoer.cn/ 本文仅用于学习记录&#xff0c;不存在任何商业用途&#xff0c;如侵删【已联系过文档作者】 文章目录非零基础自学Golang2 开发环境2.5 第一个Go 程序2.5.1 Hello World2 开发环境 2.5 第一个Go 程序 2…

IDEA创建Java Web项目

✅作者简介&#xff1a;热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&#xff1a;JAVA开发者…

【电商】跨境电商「保税」业务(附支付宝、微信对接指南)

​电商作为产品的一个大类&#xff0c;很多业务相关的东西都比社交、工具类产品更为复杂&#xff1b;而且涉及到经济、支付等环节&#xff0c;让很多新人头疼不已。这篇文章作者从跨境电商的「保税」业务出发&#xff0c;以支付宝、微信的对接为例&#xff0c;为你详解跨境电商…

Python之面向对象

目录 一、类的定义 二、魔法方法 三、属性管理 四、封装 五、继承&多态 一、类的定义 # 类封装 class Dog:name"修狗"age3dDog() print(fname is {d.name} ,age is {d.age}) # python与java关于类封装的区别 d.sex"母" print(fname is {d.name} ,…

逸飞激光在科创板IPO过会:前三季度营收约3亿元,同比增长59%

近日&#xff0c;上海证券交易所科创板披露的信息显示&#xff0c;武汉逸飞激光股份有限公司&#xff08;下称“逸飞激光”&#xff09;获得上市委会议通过。据贝多财经了解&#xff0c;逸飞激光于2022年6月24日在科创板递交招股书。 本次冲刺科创板上市&#xff0c;逸飞激光计…

【大数据入门核心技术-Hadoop】Hadoop高可用集群搭建

目录 一、Hadoop部署的三种方式 1、Standalone mode&#xff08;独立模式&#xff09; 2、Pseudo-Distributed mode&#xff08;伪分布式模式&#xff09; 3、Cluster mode&#xff08;集群模式&#xff09; 二、准备工作 1、先完成zk高可用搭建 2、/etc/hosts增加内容 …

数据科学家赚多少?数据全分析与可视化 ⛵

&#x1f4a1; 作者&#xff1a;韩信子ShowMeAI &#x1f4d8; 数据分析实战系列&#xff1a;https://www.showmeai.tech/tutorials/40 &#x1f4d8; AI 岗位&攻略系列&#xff1a;https://www.showmeai.tech/tutorials/47 &#x1f4d8; 本文地址&#xff1a;https://www…

[附源码]计算机毕业设计JAVA一点到家小区微帮服务系统

[附源码]计算机毕业设计JAVA一点到家小区微帮服务系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM…

Linux简单命令

Linux简单命令 现在写几个使用Linux时最常使用的简单命令 1.将一个文件复制到另一个文件夹内&#xff0c;比如将backboneA复制到backboneB路径下&#xff1a; cp -r /文件夹路径backboneA/* /文件夹路径backboneB/2.查看当前目录下有多少个文件及文件夹&#xff0c;需在终端输…

C++闲谈04——设计模式

C闲谈04——设计模式 单例模式 饿汉式单例模式 #include<iostream> #include<mutex>using namespace std; mutex mtx;class SingleTon{ public:static SingleTon* GetInstance() {if (instance nullptr) {lock_guard<mutex> lk(mtx); // 不是尖括号inst…

修复 爱普生 EPSON L4156 打印机 无法打印,开关 WIFI 墨水 三个灯同时闪烁的问题

L4151 L4153 L4156 L4158 L4163 L4165 L4166 L4168 L4169 喷墨一体机清零图解 清零前请取消打印任务&#xff0c;打印机用 USB 线接电脑并开启 工具下载地址 EPSON打印机清零软件1.双击[Resetter.exe]启动软件,点击[Select],选择 Port 打 印机型号&#xff0c;然后点[OK]&#…

第8章 注意力机制与外部记忆

系列文章目录 第1章 绪论 第2章 机器学习概述 第3章 线性模型 第4章 前馈神经网络 第5章 卷积神经网络 第6章 循环神经网络 第7章 网络优化与正则化 第8章 注意力机制与外部记忆 第9章 无监督学习 第10章 模型独立的学习方式 第11章 概率图模型 第12章 深度信念网络 第13章 深…