Spring之Bean生命周期源码解析-Bean销毁

2023-11-01

  这篇文章是我在系统学习Spring源码之后,基于自己对Spring源码的理解,来详细分析Spring之Bean的销毁过程。

目录

前言

一、注册有销毁逻辑的Bean

1.判断当前Bean是否需要销毁

1.1. 判断当前Bean是否有销毁方法

1.2. 判断有没有DestructionAwareBeanPostProcessor,并且DestructionAwareBeanPostProcessor.requiresDestruction()方法返回true

2.注册DisposableBean

二、Bean销毁过程

1.容器关闭

2.执行doClose()


前言

在Bean创建的过程中,在最后(初始化之后),有一个步骤是去注册DisposableBean,原型Bean是不会注册成为DisposableBean的,因为Spring容器中是不会存原型Bean的,Spring是通过requiresDestruction()方法来判断该Bean是否需要销毁,对需要销毁的Bean,封装成DisposableBeanAdapter对象,最后调用registerDisposableBean()方法将DisposableBeanAdapter对象放入disposableBeans中,当Spring容器关闭的时候,可以直接从该map中取出定义了销毁逻辑的Bean,执行它们销毁的方法;

protected void  registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
		AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
		//requiresDestruction(bean, mbd),判断当前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));
			}
		}
}

一、注册有销毁逻辑的Bean

1.判断当前Bean是否需要销毁

protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {
		//判断当前bean在销毁时候,有没有定义有关销毁的某些方法,不是所有的bean在销毁的时候都要去执行有关销毁的逻辑
		return (bean.getClass() != NullBean.class && (DisposableBeanAdapter.hasDestroyMethod(bean, mbd) ||
				(hasDestructionAwareBeanPostProcessors() && DisposableBeanAdapter.hasApplicableProcessors(
						bean, getBeanPostProcessorCache().destructionAware))));
	}

1.1. 判断当前Bean是否有销毁方法

 1) 如果当前bean实现了DisposableBean或AutoCloseable接口,重写接口中的destroy()和close()方法,这两个方法都是销毁方法;

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

private static String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
		String destroyMethodName = beanDefinition.resolvedDestroyMethodName;
		if (destroyMethodName == null) {
			destroyMethodName = beanDefinition.getDestroyMethodName();
			if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||
					(destroyMethodName == null && bean instanceof AutoCloseable)) {
				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);
}

2)如果没有实现这两个接口,则判断当前Bean的RootBeanDefinition中是否设置了销毁方法的名字,设置销毁方法名有两种场景:

场景一:自定义的方法名字,Spring会将此方法的名字作为销毁方法的名字;

场景二:指定了特定的销毁方法的名字:"(inferred)",则会将该Bean中close()和shutdown()作为销毁方法(前提是Bean里面有这两个方法);

@Component
public class MyMergedBeanDefinitionPostProcessor1 implements MergedBeanDefinitionPostProcessor {

	@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		if (beanName.equals("xxx")) {
            //设置Spring指定的特定的名字"(inferred)"
			beanDefinition.setDestroyMethodName("(inferred)");、
            //自定义销毁方法的名字
			beanDefinition.setDestroyMethodName("customDestory");
		}
	}

}

初始化和销毁方法名都是在创建Bean过程中的合并后的BeanDefination的后置处理阶段设置的,即MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition()中完成的。

1.2. 判断有没有DestructionAwareBeanPostProcessor,并且DestructionAwareBeanPostProcessor.requiresDestruction()方法返回true

· DestructionAwareBeanPostProcessor接口主要用于Bean销毁,其中的requiresDestruction()判断Bean是否需要销毁,postProcessBeforeDestruction()是实现具体的销毁逻辑,而InitDestroyAnnotationBeanPostProcessor就是DestructionAwareBeanPostProcessor的一个具体实现;

· 这里主要针对@PreDestroy注解,它主要用来定义销毁方法(被该注解修饰的方法都是销毁方法),该注解的处理就是在InitDestroyAnnotationBeanPostProcessor类中完成,它会缓存每个Bean以及它的父类中被@PreDestroy修饰的方法;

public static boolean hasApplicableProcessors(Object bean, List<DestructionAwareBeanPostProcessor> postProcessors) {
		if (!CollectionUtils.isEmpty(postProcessors)) {
			for (DestructionAwareBeanPostProcessor processor : postProcessors) {
				//确定给定的 bean实例,有没有定义销毁逻辑
				if (processor.requiresDestruction(bean)) {
					return true;
				}
			}
		}
		return false;
}

· @PostConstruct和@PreDestroy注解的扫描,是在buildLifecycleMetadata()方法中完成并进行分类缓存的,这一步骤在创建Bean过程中的初始化阶段就已完成,这里只需要判断是否有@PreDestroy定义的销毁方法,判断当前Bean是否需要销毁;

public boolean requiresDestruction(Object bean) {
		return findLifecycleMetadata(bean.getClass()).hasDestroyMethods();
}	

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

2.注册DisposableBean

注册销毁的Bean,disposableBeans中缓存的是DisposableBeanAdapter对象,而不是当前正在创建的Bean对象,无论该Bean是实现了DisposableBean或AutoCloseable接口,或者是通过BeanDifinition后置处理指定了”(inferred)“销毁方法名或其它名字的销毁方法, 还是通过@PreDestroy指定了销毁方法,这里都会将Bean适配成一个DisposableBeanAdapter对象;

// 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));

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

说明:这里涉及到一个设计模式:适配器模式 ,在销毁时,Spring会找出定义了销毁逻辑的Bean。 但是我们在定义一个Bean时,如果这个Bean实现了DisposableBean接口,或者实现了 AutoCloseable接口,或者在BeanDefinition中指定了destroyMethodName,那么这个Bean都属 于“DisposableBean”,这些Bean在容器关闭时都要调用相应的销毁方法。 所以,这里就需要进行适配,将实现了DisposableBean接口、或者AutoCloseable接口等适配成实现了DisposableBean接口,所以就用到了DisposableBeanAdapter。

DisposableBeanAdapter的构造方法

在DisposableBeanAdapter的构造方法中,会推断出销毁方法,并过滤出所有实现了DestructionAwareBeanPostProcessor接口且requiresDestruction()方法返回true的DestructionAwareBeanPostProcessor,销毁的时候会调用它们的postProcessBeforeDestruction()方法;

public DisposableBeanAdapter(Object bean, String beanName, RootBeanDefinition beanDefinition,
			List<DestructionAwareBeanPostProcessor> postProcessors, @Nullable AccessControlContext acc) {

		Assert.notNull(bean, "Disposable bean must not be null");
		this.bean = bean;
		this.beanName = beanName;
		this.invokeDisposableBean =
				(this.bean instanceof DisposableBean && !beanDefinition.isExternallyManagedDestroyMethod("destroy"));
		this.nonPublicAccessAllowed = beanDefinition.isNonPublicAccessAllowed();
		this.acc = acc;
		String destroyMethodName = inferDestroyMethodIfNecessary(bean, beanDefinition);
		if (destroyMethodName != null && !(this.invokeDisposableBean && "destroy".equals(destroyMethodName)) &&
				!beanDefinition.isExternallyManagedDestroyMethod(destroyMethodName)) {
			this.destroyMethodName = destroyMethodName;
			Method destroyMethod = determineDestroyMethod(destroyMethodName);
			if (destroyMethod == null) {
				if (beanDefinition.isEnforceDestroyMethod()) {
					throw new BeanDefinitionValidationException("Could not find a destroy method named '" +
							destroyMethodName + "' on bean with name '" + beanName + "'");
				}
			}
			else {
				if (destroyMethod.getParameterCount() > 0) {
					Class<?>[] paramTypes = destroyMethod.getParameterTypes();
					if (paramTypes.length > 1) {
						throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
								beanName + "' has more than one parameter - not supported as destroy method");
					}
					else if (paramTypes.length == 1 && boolean.class != paramTypes[0]) {
						throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
								beanName + "' has a non-boolean parameter - not supported as destroy method");
					}
				}
				destroyMethod = ClassUtils.getInterfaceMethodIfPossible(destroyMethod);
			}
			this.destroyMethod = destroyMethod;
		}
		this.beanPostProcessors = filterPostProcessors(postProcessors, bean);
}

private static List<DestructionAwareBeanPostProcessor> filterPostProcessors(
			List<DestructionAwareBeanPostProcessor> processors, Object bean) {

		List<DestructionAwareBeanPostProcessor> filteredPostProcessors = null;
		if (!CollectionUtils.isEmpty(processors)) {
			filteredPostProcessors = new ArrayList<>(processors.size());
			for (DestructionAwareBeanPostProcessor processor : processors) {
				if (processor.requiresDestruction(bean)) {
					filteredPostProcessors.add(processor);
				}
			}
		}
		return filteredPostProcessors;
}

二、Bean销毁过程

1.容器关闭

在Spring容器关闭的时候,会去销毁所有的单例Bean,只要是单例对象,不管有没有定义销毁的逻辑,都是要销毁的,只是定义了销毁逻辑的单例Bean在销毁之前,Spring会调用它们定义的销毁逻辑,Spring容器关闭触发Bean销毁的两种方式,如下:

ApplicationContext context= new AnnotationConfigApplicationContext(AppConfig.class);
//spring容器关闭的时候,会触发销毁方法
context.close();
//不用手动调用context.close()方法,可以向JVM里面注册一个关闭钩子,这样也可以触发销毁方法,,这个关闭钩子就是一个线程
context.registerShutdownHook(); 

以上两种方式,都会调用doClose(),doClose()会去调执行销毁Bean的方法。

2.执行doClose()

doClose()中会调用destroyBeans(),而在destroySingletons()中会取出disposableBeans缓存中定义了销毁逻辑的Bean的beanName,然后遍历进行销毁

protected void destroyBeans() {
		getBeanFactory().destroySingletons();
}

public void destroySingletons() {
		if (logger.isTraceEnabled()) {
			logger.trace("Destroying singletons in " + this);
		}
		synchronized (this.singletonObjects) {
			this.singletonsCurrentlyInDestruction = true;
		}

		String[] disposableBeanNames;
		synchronized (this.disposableBeans) {
			disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
		}
		for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
			destroySingleton(disposableBeanNames[i]);
		}

		this.containedBeanMap.clear();
		this.dependentBeanMap.clear();
		this.dependenciesForBeanMap.clear();

		clearSingletonCache();
}

在进行销毁的时候,先从单例池等缓存中移除Bean,然后从disposableBeans移除当前DisposableBean并获取该对象,最后调用destroyBean(beanName, disposableBean)执行对象的销毁逻辑;在销毁当前Bean的时候,会获取依赖当前Bean的其他Bean的beanName,然后递归调用destroySingleton()方法,保证依赖当前Bean的其他Bean先销毁,在进行销毁时,会先调用DisposableBean的destroy()方法,然后再去调用其它的销毁逻辑,其它的销毁逻辑基本就是从各种缓存中根据BeanName,清除缓存;

public void destroySingleton(String beanName) {
		// Remove a registered singleton of the given name, if any.
		// 先从单例池中移除掉
		removeSingleton(beanName);

		// Destroy the corresponding DisposableBean instance.
		DisposableBean disposableBean;
		synchronized (this.disposableBeans) {
			disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
		}
		destroyBean(beanName, disposableBean);
}

protected void destroyBean(String beanName, @Nullable DisposableBean bean) {

		// dependentBeanMap表示某bean被哪些bean依赖了
		// 所以现在要销毁某个bean时,如果这个Bean还被其他Bean依赖了,那么也得销毁其他Bean
		// Trigger destruction of dependent beans first...
		Set<String> dependencies;
		synchronized (this.dependentBeanMap) {
			// Within full synchronization in order to guarantee a disconnected Set
			dependencies = this.dependentBeanMap.remove(beanName);
		}
		if (dependencies != null) {
			if (logger.isTraceEnabled()) {
				logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
			}
			for (String dependentBeanName : dependencies) {
				destroySingleton(dependentBeanName);
			}
		}

		// Actually destroy the bean now...
		if (bean != null) {
			try {
				//会调用DisposableBeanAdapter对象的destory方法
				bean.destroy(); 
			}
			catch (Throwable ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Destruction of bean with name '" + beanName + "' threw an exception", ex);
				}
			}
		}

		// Trigger destruction of contained beans...
		Set<String> containedBeans;
		synchronized (this.containedBeanMap) {
			// Within full synchronization in order to guarantee a disconnected Set
			containedBeans = this.containedBeanMap.remove(beanName);
		}
		if (containedBeans != null) {
			for (String containedBeanName : containedBeans) {
				destroySingleton(containedBeanName);
			}
		}

		// Remove destroyed bean from other beans' dependencies.
		synchronized (this.dependentBeanMap) {
			for (Iterator<Map.Entry<String, Set<String>>> it = this.dependentBeanMap.entrySet().iterator(); it.hasNext();) {
				Map.Entry<String, Set<String>> entry = it.next();
				Set<String> dependenciesToClean = entry.getValue();
				dependenciesToClean.remove(beanName);
				if (dependenciesToClean.isEmpty()) {
					it.remove();
				}
			}
		}

		// Remove destroyed bean's prepared dependency information.
		this.dependenciesForBeanMap.remove(beanName);
	}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring之Bean生命周期源码解析-Bean销毁 的相关文章

随机推荐

  • BUUCTF-Misc部分

    二维码 附件压缩包下载下来是一个二维码图片 直接用工具查看 并没有直接给出flag 猜测是压缩包文件 使用winhex查看 明显的提示pk 压缩文件 可以用binwalk命令分离文件 也可以直接修改后缀 部分可能会存在损坏文件的问题 先复制
  • JAVA 获取实时汇率

    聚合数据中有很多我们会用到的数据 下面是获取实时汇率的方式 key值聚合数据注册后可获取 需要练习借用的 请评论回复 public void exchangeRate String urlIDR http op juhe cn onebox
  • 安装pip3

    安装pip3 yum install epel release yum install python36 y yum install python36 setuptools easy install 3 6 pip
  • uniapp之小程序setData迁移

    uniapp修改data中的值 第一次用uniapp 踩坑了 坑1 不能用this setData 得用this set 对象 属性名 值 坑2 data的属性得用this data做目标对象 贴上代码 export default dat
  • CCF-CSP真题《202303-1 田地丈量》思路+python,c++,java满分题解

    想查看其他题的真题及题解的同学可以前往查看 CCF CSP真题附题解大全 试题编号 202303 1 试题名称 田地丈量 时间限制 1 0s 内存限制 512 0MB 问题描述 问题描述 西西艾弗岛上散落着 n 块田地 每块田地可视为平面直
  • 华为校招机试题-查找充电设备组合-2023年

    题目描述 某个充电站 可提供n个充电设备 每个充电设备均有对应的输出功率 任意个充电设备组合的输出功率总和 均构成功率集合P的1个元素 功率集合P的最优元素 表示最接近充电站最大输出功率p max的元素 输入描述 输入为3行 第1行为充电设
  • C++中引用(&)的用法和应用实例

    对于习惯使用C进行开发的朋友们 在看到c 中出现的 符号 可能会犯迷糊 因为在C语言中这个符号表示了取地址符 但是在C 中它却有着不同的用途 掌握C 的 符号 是提高代码执行效率和增强代码质量的一个很好的办法 在 c 学习提高篇 3 隐式指
  • 代码审查审什么

    代码审查审什么 看着很多人做代码审查重点审格式和命名 制定的代码规范也主要偏重代码格式和命名 我真想骂一句操蛋 这真是浪费时间又解决不了问题 此篇文章就是骂完操蛋后奋笔快速敲下来的 有不妥之处请大家谅解 一 目的 为啥要花费时间要搞人工代码
  • 再学C语言(一)——关于ifndef

    关于ifndef的解释 ifndef 是 if not defined 的简写 是预处理功能 宏定义 文件包含 条件编译 当中的条件编译 可以根据是否已经定义了一个变量来进行分支选择 ifndef的作用 防止头文件的重复包含和编译 把头文件
  • LeetCode-1615. 最大网络秩

    1615 最大网络秩 题目说明 n 座城市和一些连接这些城市的道路 roads 共同组成一个基础设施网络 每个 roads i ai bi 都表示在城市 ai 和 bi 之间有一条双向道路 两座不同城市构成的 城市对 的 网络秩 定义为 与
  • Windows中你用过的Git账户藏在哪里?

    最近换工作了 新公司也没有自己的Git服务器 所以还是选择了之前用过的Coding net 之前注册的账户用的是上一家公司的邮箱 为免出现不愉快的事情 重新注册了一个账户 顺顺利利地新建项目 clone push 晚上回到家 想clone一
  • mysql增量抽取方法_DataX增量抽取数据

    datax增量项目地址 datax作业配置文件 datax json job setting speed channel 16 content reader name mysqlreader parameter splitPk id use
  • 【数据结构】带头结点的单链表

    数据结构 带头结点的单链表 链表 逻辑连续 物理上不一定连续 带头结点的单链表 结构体 定义结构体 typedef int ELEM TYPE typedef struct Node ELEM TYPE mdata struct Node
  • 九、C++编译过程详解

    九 C 编译过程详解 1 什么是编译器 2 多文件编译与链接 3 为什么需要构建系统 Makefile 4 CMake CMakeLists txt 5 CMake中的静态库和动态库 1 什么是编译器 编译器是一个根据源代码生成机器码的程序
  • Windows获取密码及hash

    前言 在拿到一台 Windows 的管理员权限以后 可以通过多种方法获取 Windows 系统的明文密码或者 hash 值 这将有利于我们在内网中扩大渗透范围 0x01 Mimikatz Mimikat是一个法国人写的轻量级调试器 Mimi
  • 视频压缩之冗余

    视频压缩之冗余 对于数字视频信号 数据量很大 不管是存储还是传输的需要 做压缩处理是必须的 下面我们会做进一步阐述 以记录数字视频的YUV分量格式为例 YUV分别代表亮度与两个色差信号 例如对于现有的PAL制电视系统 其亮度信号采样频率为1
  • 整流器+逆变器。 前级采用PWM整流器,采用双闭环前馈解耦控制

    整流器 逆变器 前级采用PWM整流器 采用双闭环前馈解耦控制 实现并网单位功率因数 稳定直流电压 后级采用两电平逆变器 通过双闭环前馈解耦控制 稳定输出电压 整个仿真环境完全离散化 运行时间更快 主电路与控制部分以不同的步长运行 更加贴合实
  • CentOS7.4 离线升级openssh8.4

    CentOS7 4 离线升级openssh8 4 前言 工作中需要离线升级openssh 网上一些资料说要先安装telnet 这里省略 大家可以先安装telnet 预防更新ssh失败 下载openssl安装包 去https www open
  • 数据分析——时间序列分析模型(AR,MA,ARMA,ARIMA)

    1 概述 时间序列是某个时间段或者某些时间点对应的不同数值的数值对 这些数值对只有两个具体数据 时间要素 数值要素 时间要素可以是某一个时间段或者某一个时刻 例如一个杂货铺一周 七天 的销售额为时间段的时间要素 而一天二十四小时每个整点所对
  • Spring之Bean生命周期源码解析-Bean销毁

    这篇文章是我在系统学习Spring源码之后 基于自己对Spring源码的理解 来详细分析Spring之Bean的销毁过程 目录 前言 一 注册有销毁逻辑的Bean 1 判断当前Bean是否需要销毁 1 1 判断当前Bean是否有销毁方法 1