Bean生命周期销毁阶段

其他阶段的解析参考:

概括

bean 对象在销毁时,由 ApplicationContext 发起关闭动作。在销毁 bean 的阶段,由 BeanFactory 取出所有单实例 bean ,并逐个销毁。

发起关闭后,会在DefaultSingletonBeanRegistry类的destroyBean方法中触发对每一个Bean的实际销毁动作,其销毁的步骤为:

  1. 先销毁依赖当前Bean的其他Bean,即当销毁一个Bean时,依赖此Bean的其他Bean需先被销毁
  2. 进行Bean销毁方法的执行,先后执行顺序是:
    • 如果bean中标记了@PreDestroy注解的销毁方法,则最先执行
    • 如果bean实现DisposableBean接口并重写了destroy方法,则第二个执行
    • 如果bean实现invokeAutoCloseablen接口并重写了close方法,第三个执行
    • 如果bean中声明了自定义destroy-method方法,最后执行
  3. 处理内部bean的销毁。当一个Bean存在内部Bean时,内部Bean也要进行销毁
  4. 清理Bean的依赖关系,从Bean的依赖集合中移除当前被销毁的Bean

源码入口

由容器关闭触发,在AbstractApplicationContextclose方法内:

image-20251031134555741

doClose方法内执行destroyBeans

image-20251031134835905

destroyBeans再执行到destroySingletons,开启单实例Bean的销毁:

image-20251031134901838

执行销毁的destroySingletons方法位于DefaultListableBeanFactory类中,其源码如下:

@Override
public void destroySingletons() {
    // 进入父类处理,这里是核心逻辑
	super.destroySingletons();
	// 清空所有手动注册的单例Bean名称
	updateManualSingletonNames(Set::clear, set -> !set.isEmpty());
    // 清除Bean类型与Bean名称的映射,方法内会清除两个集合,这两个集合都是以类型为key,bean名为value
	clearByTypeCache();
}

下面介绍Bean销毁的核心逻辑


执行销毁

核心销毁步骤在其父类中,进入DefaultListableBeanFactory父类DefaultSingletonBeanRegistrydestroySingletons方法:

public void destroySingletons() {
	if (logger.isTraceEnabled()) {
		logger.trace("Destroying singletons in " + this);
	}
    // 同步锁加在一级缓存singletonObjects上
	synchronized (this.singletonObjects) {
		this.singletonsCurrentlyInDestruction = true;
	}

	String[] disposableBeanNames;
    // disposableBeans中存放了需要被销毁的bean,在上一章介绍Bean初始化阶段时被存入
    // 位置在`AbstractAutowireCapableBeanFactory`中的`doCreateBean`方法中的`registerDisposableBeanIfNecessary`
	synchronized (this.disposableBeans) {
        // 取出需销毁bean的名称,由set转为数组
		disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
	}
    
    // 遍历每一个执行销毁逻辑
	for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
        // 此处是单实例Bean的销毁方法执行
		destroySingleton(disposableBeanNames[i]);
	}

    // 清空所有缓存
	this.containedBeanMap.clear();
	this.dependentBeanMap.clear();
	this.dependenciesForBeanMap.clear();

	clearSingletonCache();
}

由以上代码可知,需要销毁的Bean会从disposableBeans中被取出。在前面介绍Bean生命周期初始化章节的最后,会在AbstractAutowireCapableBeanFactorydoCreateBean方法执行一个用来注册销毁处理的 registerDisposableBeanIfNecessary,此方法会检查Bean是否有销毁逻辑,如果有则将Bean封装为DisposableBeanAdapter对象并缓存起来。在真正执行到每一个Bean的销毁时,执行的就是DisposableBeanAdapterdestroy方法,下面会进行介绍。

上面代码的核心是 执行destroySingleton,它会挨个执行每一个单实例的销毁,进入其中:

public void destroySingleton(String beanName) {
    
	// 通过bean名,将此bean所涉及的三级缓存全部清空
	removeSingleton(beanName);

	// 从disposableBeans中移除要销毁的bean
	DisposableBean disposableBean;
	synchronized (this.disposableBeans) {
		disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
	}
    
    // 调用销毁bean的动作,这里是真正执行销毁的代码
	destroyBean(beanName, disposableBean);
}

然后进入实际执行Bean‘销毁动作的destroyBean方法源码中:

主要分为四步进行销毁处理:

  1. 先销毁依赖当前Bean的其他Bean,即当销毁一个Bean时,依赖此Bean的其他Bean需先被销毁
  2. 进行Bean销毁方法的执行,先后执行的是:标记了@PreDestroy注解的销毁方法、实现DisposableBean接口后重写的destroy方、实现了invokeAutoCloseablen接口的close方法、bean中声明destroy-method方法
  3. 处理内部bean的销毁。当一个Bean存在内部Bean时,内部Bean也要进行销毁
  4. 清理Bean的依赖关系,从Bean的依赖集合中移除当前被销毁的Bean
protected void destroyBean(String beanName, @Nullable DisposableBean bean) {

    // 第一步
    // 销毁一个bean时,首先要销毁依赖它的bean
	Set<String> dependencies;
	synchronized (this.dependentBeanMap) {
		// 通过bean名删除该bean所依赖的bean,依赖bean的bean名保存在dependentBeanMap中
		dependencies = this.dependentBeanMap.remove(beanName);
	}
	if (dependencies != null) {
		if (logger.isTraceEnabled()) {
			logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
		}
        // dependentBeanMap中移除后会将bean名返回到set中,这里遍历set,递归删除每一个依赖的bean
		for (String dependentBeanName : dependencies) {
			destroySingleton(dependentBeanName);
		}
	}

    
    // 第二步
    // bean不为空时,进行bean销毁方法的回调
	if (bean != null) {
		try {
            // 调用`DisposableBeanAdapter`实现的destroy方法
            // 这里就是对应注册销毁处理的后续,在将有销毁逻辑的bean封装为`DisposableBeanAdapter`对象后由此处执行销毁动作
			bean.destroy();
		}
		catch (Throwable ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Destruction of bean with name '" + beanName + "' threw an exception", ex);
			}
		}
	}
  
    
    // 第三步  
    // 处理内部bean的销毁。内部bean区别于注入,内部bean只能其外部bean独占,而依赖注入的bean是容器全局共享的
	Set<String> containedBeans;
	synchronized (this.containedBeanMap) {
		// 通过bean名删除其内部bean,并取出内部bean名
		containedBeans = this.containedBeanMap.remove(beanName);
	}
	if (containedBeans != null) {
        // 遍历内部bean名执行销毁
		for (String containedBeanName : containedBeans) {
			destroySingleton(containedBeanName);
		}
	}

 
    // 第四步
	// 清理Bean的依赖关系,从所有其他Bean的依赖集合中移除当前被销毁的Bean
    // 因为当前Bean要被销毁,则需要从dependentBeanMap的value中(set集合)移除当前bean名称
	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);
}

下面分步骤解析destroyBean的销毁处理


1. 销毁依赖此Bean的其他Bean

先看DefaultSingletonBeanRegistry.destroyBean方法中的第一步。这里销毁的是依赖当前bean的其他bean,会通过dependentBeanMap来获取要销毁的bean。

假设当前要销毁的Bean为A,B依赖了A,则源码中要先销毁B,即先销毁其他依赖于A的Bean,会从dependentBeanMap集合中寻找key为A的value,取出的value就是所有依赖于A的bean名称集合:

  • dependentBeanMap集合位于DefaultSingletonBeanRegistry类中,它是存储bean之间依赖关系的集合,Key为被依赖的Bean名称,Value为所有直接依赖该Bean的组件名Set集合:

    private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
    

下面是destroyBean方法中涉及此操作的源码:

	// 销毁一个bean时,首先要销毁依赖它的其他bean
	Set<String> dependencies;
	synchronized (this.dependentBeanMap) {
		// 通过bean名删除依赖于该bean的其他bean,那些bean的名称保存在dependentBeanMap中
		dependencies = this.dependentBeanMap.remove(beanName);
	}
	if (dependencies != null) {
		if (logger.isTraceEnabled()) {
			logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
		}
        // dependentBeanMap中移除后会将bean名返回到set中,这里遍历set,递归删除每一个依赖的bean
		for (String dependentBeanName : dependencies) {
			destroySingleton(dependentBeanName);
		}
	}

依赖关系是如何存储到dependentBeanMap中的

第一处存储: 在前面介绍Bean的实例化阶段中,开始创建Bean时,会先获取当前Bean所依赖的其它bean,并对依赖关系进行注册。

源码位于AbstractBeanFactorydoGetBean方法的第320行:

image-20251030113159146

registerDependentBean方法的第一个参数是被依赖的bean名,第二个参数是要创建的bean名。如当前创建的Bean为A,A通过构造方法注入了B,即A依赖B,那么此方法的dep参数是B,beanName参数是A。

在存入dependentBeans时,以被依赖的bean名为key,当前创建的bean名为value存入,下面是其在DefaultSingletonBeanRegistry类中的方法源码:

public void registerDependentBean(String beanName, String dependentBeanName) {
	String canonicalName = canonicalName(beanName);

    // 这里是存入dependentBeans的操作
	synchronized (this.dependentBeanMap) {
        // 当前依赖关系已存在直接返回,不存在则进行创建再返回
		Set<String> dependentBeans =
				this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
		if (!dependentBeans.add(dependentBeanName)) {
			return;
		}
	}

	synchronized (this.dependenciesForBeanMap) {
		Set<String> dependenciesForBean =
				this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
		dependenciesForBean.add(canonicalName);
	}
}

第二处存储:在前面介绍Bean的依赖注入阶段中,AbstractAutowireCapableBeanFactorypopulateBean方法内会使用autowireByNameautowireByType收集需注入的依赖信息,这两个方法内部都会调用registerDependentBean来向dependentBeanMap集合存入依赖关系:

image-20251030115145511


第三处存储:同样是Bean的依赖注入阶段,在实现注解依赖注入的AutowiredAnnotationBeanPostProcessor处理器的postProcessProperties方法中,会层层调用到registerDependentBean来向dependentBeanMap集合存入依赖关系。

以属性注入为例,注入时会执行postProcessProperties方法内的inject

image-20251030140341488

inject方法会调用AutowiredFieldElement.inject实现,该实现的方法内再调用resolveFieldValue

image-20251030135953152

resolveFieldValue中会调用到registerDependentBeans

image-20251030140025516

进入这个registerDependentBeans中,下图红框位置会调用DefaultSingletonBeanRegistryregisterDependentBeans实现,最终向dependentBeanMap集合存入依赖关系。

image-20251030140640137

使用方法注入时调用的实现是AutowiredMethodElement,与属性注入一样也是在其inject方法内调用到registerDependentBeansdependentBeanMap集合存入依赖关系。这里不在粘贴源码,调用方法顺序是:inject --》 resolveMethodArguments --》 registerDependentBeans


2.销毁方法回调

这里看DefaultSingletonBeanRegistry.destroyBean第二步的destroy方法处理

	// 第二步
    // bean不为空时,进行bean销毁方法的回调
	if (bean != null) {
		try {
            // 调用`DisposableBeanAdapter`实现的destroy方法
            // 在上面介绍注册销毁处理时,会将有销毁逻辑的bean封装为`DisposableBeanAdapter`对象
			bean.destroy();
		}
		catch (Throwable ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Destruction of bean with name '" + beanName + "' threw an exception", ex);
			}
		}
	}

此处执行的是Bean的销毁方法实现,如果Bean中标记了有关注解或重写了相关方法,那么按先后顺序执行的是:

  1. bean中标记了@PreDestroy注解的销毁方法
  2. bean实现DisposableBean接口后重写的destroy方法
  3. bean实现invokeAutoCloseablen接口后重写的close方法
  4. bean中声明的自定义destroy-method方法

下面是其源码解析,触发的实现为DisposableBeanAdapterdestroy

public void destroy() {
	
    // 后置处理器不为空,则调用DestructionAwareBeanPostProcessor的销毁实现
	if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
		for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
            // 此处会执行`@PreDestroy`注解标注的销毁方法
			processor.postProcessBeforeDestruction(this.bean, this.beanName);
		}
	}

    // 当bean实现了DisposableBean接口
	if (this.invokeDisposableBean) {
		// ............
		try {
			// ............
			else {
                // 回调DisposableBean接口的destroy方法
				((DisposableBean) this.bean).destroy();
			}
		}
		// catch ......		
	}

    // 当bean实现了invokeAutoCloseablen接口
	if (this.invokeAutoCloseable) {
		// ............
		try {
			// ............
			else {
                // 调用AutoCloseable的close方法
                // close方法主要用于资源的关闭(如文件、数据库连接、网络连接等)
                // try-with-resources代码块结束时就会自动触发AutoCloseable的close方法关闭资源
				((AutoCloseable) this.bean).close();
			}
		}
		// catch ......	
	}
    
    // 如果bean声明了自定义`destroy-method`方法,则回调`destroy-method`销毁方法
	else if (this.destroyMethod != null) {
		invokeCustomDestroyMethod(this.destroyMethod);
	}
    // 在上面都没有配置的情况下,通过destroyMethodName来寻找对应的自定义方法进行销毁,现代开发中基本不会走这一步。
    // 在DisposableBeanAdapter的构造函数中,会通过指定的销毁方法构建Method对象,如果无法构建或无法对其进行调用,就会兜底使用此处的destroyMethodName来尝试寻找对应的销毁方法进行销毁
	else if (this.destroyMethodName != null) {
		Method methodToInvoke = determineDestroyMethod(this.destroyMethodName);
		if (methodToInvoke != null) {
			invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke));
		}
	}
}

通过以上源码得知,bean的销毁阶段生命周期回调的顺序:@PreDestroyDisposableBeandestroy-method


3.内部bean销毁

进入DefaultSingletonBeanRegistry.destroyBean方法中的第三步处理,如果一个Bean有内部Bean,针对其内部bean递归调用destroySingleton进行销毁

	// 第三步  
    // 处理内部bean的销毁。内部bean区别于注入,内部bean只能其外部bean独占,而依赖注入的bean是容器全局共享的
	Set<String> containedBeans;
	synchronized (this.containedBeanMap) {
		// 通过bean名删除其内部bean,并取出内部bean名
		containedBeans = this.containedBeanMap.remove(beanName);
	}
	if (containedBeans != null) {
        // 遍历内部bean名执行销毁
		for (String containedBeanName : containedBeans) {
			destroySingleton(containedBeanName);
		}
	}

什么是内部bean:

  • 在Spring框架中,内部bean(也称为匿名bean)指的是在一个bean的内部定义的bean。内部bean通常用于那些只需要在另一个bean的作用域内使用的场景,而不需要在其他地方被引用。

内部bean与注入bean的区别:

  • Bean 作用域:内部Bean是匿名Bean,仅被其外部Bean独占引用;注入的Bean是容器全局Bean,有ID且可被多组件共享
  • 生命周期管理:内部Bean随父Bean创建/销毁,无独立生命周期;注入的Bean由Spring容器独立管理,拥有完整生命周期
  • 依赖可见性:内部Bean完全私有,外部无法访问其实例;注入的Bean全局可见且能被获取

4.清除依赖关系

这里是DefaultSingletonBeanRegistry.destroyBean方法的最后一步处理,从dependentBeanMap集合内的value部分(Set集合)中移除当前被销毁的bean

	// 第四步
	// 清理Bean的依赖关系,从所有其他Bean的依赖集合中移除当前被销毁的Bean
    // 因为当前Bean要被销毁,则需要从dependentBeanMap的value中(set集合)移除当前bean名称
	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();
			}
		}
	}

	// 清理依赖准备信息
	// dependenciesForBeanMap中的key是当前bean名称,value是此Bean依赖的所有Bean名称集合
	this.dependenciesForBeanMap.remove(beanName);

假设dependentBeanMap中存有以下依赖关系:

"serviceA" -> ["serviceB", "serviceC"]   # serviceB和serviceC依赖serviceA
"serviceB" -> ["serviceD"]               # serviceD依赖serviceB
"dataSource" -> ["serviceA", "serviceB"] # serviceA和serviceB依赖dataSource

当销毁 serviceA 时,需要:

  1. dataSource 的依赖集合中移除 serviceA
  2. 如果 dataSource 的依赖集合变为空,移除整个条目
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值