其他阶段的解析参考:
概括
bean 对象在销毁时,由
ApplicationContext发起关闭动作。在销毁 bean 的阶段,由BeanFactory取出所有单实例 bean ,并逐个销毁。
发起关闭后,会在DefaultSingletonBeanRegistry类的destroyBean方法中触发对每一个Bean的实际销毁动作,其销毁的步骤为:
- 先销毁依赖当前Bean的其他Bean,即当销毁一个Bean时,依赖此Bean的其他Bean需先被销毁
- 进行Bean销毁方法的执行,先后执行顺序是:
- 如果bean中标记了
@PreDestroy注解的销毁方法,则最先执行 - 如果bean实现
DisposableBean接口并重写了destroy方法,则第二个执行 - 如果bean实现
invokeAutoCloseablen接口并重写了close方法,第三个执行 - 如果bean中声明了自定义
destroy-method方法,最后执行
- 如果bean中标记了
- 处理内部bean的销毁。当一个Bean存在内部Bean时,内部Bean也要进行销毁
- 清理Bean的依赖关系,从Bean的依赖集合中移除当前被销毁的Bean
源码入口
由容器关闭触发,在
AbstractApplicationContext的close方法内:

doClose方法内执行destroyBeans:

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

执行销毁的
destroySingletons方法位于DefaultListableBeanFactory类中,其源码如下:
@Override
public void destroySingletons() {
// 进入父类处理,这里是核心逻辑
super.destroySingletons();
// 清空所有手动注册的单例Bean名称
updateManualSingletonNames(Set::clear, set -> !set.isEmpty());
// 清除Bean类型与Bean名称的映射,方法内会清除两个集合,这两个集合都是以类型为key,bean名为value
clearByTypeCache();
}
下面介绍Bean销毁的核心逻辑
执行销毁
核心销毁步骤在其父类中,进入
DefaultListableBeanFactory父类DefaultSingletonBeanRegistry的destroySingletons方法:
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生命周期初始化章节的最后,会在AbstractAutowireCapableBeanFactory的doCreateBean方法执行一个用来注册销毁处理的 registerDisposableBeanIfNecessary,此方法会检查Bean是否有销毁逻辑,如果有则将Bean封装为DisposableBeanAdapter对象并缓存起来。在真正执行到每一个Bean的销毁时,执行的就是DisposableBeanAdapter的destroy方法,下面会进行介绍。
上面代码的核心是 执行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方法源码中:
主要分为四步进行销毁处理:
- 先销毁依赖当前Bean的其他Bean,即当销毁一个Bean时,依赖此Bean的其他Bean需先被销毁
- 进行Bean销毁方法的执行,先后执行的是:标记了
@PreDestroy注解的销毁方法、实现DisposableBean接口后重写的destroy方、实现了invokeAutoCloseablen接口的close方法、bean中声明destroy-method方法 - 处理内部bean的销毁。当一个Bean存在内部Bean时,内部Bean也要进行销毁
- 清理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,并对依赖关系进行注册。
源码位于AbstractBeanFactory的doGetBean方法的第320行:

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的依赖注入阶段中,AbstractAutowireCapableBeanFactory的populateBean方法内会使用autowireByName与autowireByType收集需注入的依赖信息,这两个方法内部都会调用registerDependentBean来向dependentBeanMap集合存入依赖关系:

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

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

resolveFieldValue中会调用到registerDependentBeans:

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

使用方法注入时调用的实现是AutowiredMethodElement,与属性注入一样也是在其inject方法内调用到registerDependentBeans向dependentBeanMap集合存入依赖关系。这里不在粘贴源码,调用方法顺序是: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中标记了有关注解或重写了相关方法,那么按先后顺序执行的是:
- bean中标记了
@PreDestroy注解的销毁方法 - bean实现
DisposableBean接口后重写的destroy方法 - bean实现
invokeAutoCloseablen接口后重写的close方法 - bean中声明的自定义
destroy-method方法
下面是其源码解析,触发的实现为DisposableBeanAdapter的destroy:
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的销毁阶段生命周期回调的顺序:@PreDestroy → DisposableBean → destroy-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 时,需要:
- 从
dataSource的依赖集合中移除serviceA - 如果
dataSource的依赖集合变为空,移除整个条目
1249

被折叠的 条评论
为什么被折叠?



