SpringBoot关闭过程中是如何销毁一个DisposableBean的?

简介: SpringBoot关闭过程中是如何销毁一个DisposableBean的?

接上文SpringBoot关闭时都做了哪些事?,本文我们详细分析一下DisposableBean的destroy过程。

DefaultSingletonBeanRegistrydestroySingleton方法。

public void destroySingleton(String beanName) {
  // Remove a registered singleton of the given name, if any.
  // 从缓存中移除当前beanName
  removeSingleton(beanName);
  // Destroy the corresponding DisposableBean instance.
  // 从disposableBeans集合中移除当前 beanName
  DisposableBean disposableBean;
  synchronized (this.disposableBeans) {
    disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
  }
  // 这是核心方法
  destroyBean(beanName, disposableBean);
}

disposableBeans里面存放的是beanName以及对应的DisposableBeanAdapter实例。其他方法我们过一下即可,这里我们着重分析destroyBean(beanName, disposableBean);

【1】核心方法DefaultSingletonBeanRegistry的destroyBean

// DefaultSingletonBeanRegistry
protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
  // Trigger destruction of dependent beans first...
  Set<String> dependencies;
  //dependentBeanMap存放的是哪些bean依赖于key
  synchronized (this.dependentBeanMap) {
    // Within full synchronization in order to guarantee a disconnected Set
    dependencies = this.dependentBeanMap.remove(beanName);
  }
  // 首先触发那些依赖于当前beanName的bean的销毁流程
  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...
  // bean销毁的过程入口,触发DisposableBeanAdapter的destroy方法
  if (bean != null) {
    try {
      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...
  // 获取当前beanName对应的containedBeans,如果有,则遍历触发销毁流程
  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.
  // 遍历dependentBeanMap的value,移除掉当前beanName。
  //之后如果value为空,其从dependentBeanMap移除掉当前entry
  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.
  // 从 dependenciesForBeanMap中移除当前beanName对应的entry
  this.dependenciesForBeanMap.remove(beanName);
}

方法逻辑梳理如下:

  • 首先触发那些依赖于当前beanName的bean的销毁流程
  • bean.destroy();触发DisposableBeanAdapter的destroy方法
  • 获取当前实例维护的DestructionAwareBeanPostProcessor 触发其postProcessBeforeDestruction方法,如果有@PreDestroy注解的方法,这时会被触发。
  • 根据invokeDisposableBean判断是否触发destroy方法
  • 尝试触发其自定义destroy method
  • 获取当前beanName对应的containedBeans,如果有,则遍历触发销毁流程
  • 遍历dependentBeanMap的value,移除掉当前beanName。之后如果value为空,其从dependentBeanMap移除掉当前entry
  • dependenciesForBeanMap中移除当前beanName对应的entry

【2】DisposableBeanAdapter

上面提到了bean.destroy();触发DisposableBeanAdapter的destroy方法,方法如下所示:

@Override
public void destroy() {
//获取当前实例维护的DestructionAwareBeanPostProcessor 触发其postProcessBeforeDestruction方法
  if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
    for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
      processor.postProcessBeforeDestruction(this.bean, this.beanName);
    }
  }
// 是否触发destroy方法
  if (this.invokeDisposableBean) {
    if (logger.isTraceEnabled()) {
      logger.trace("Invoking destroy() on bean with name '" + this.beanName + "'");
    }
    try {
      if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
          ((DisposableBean) this.bean).destroy();
          return null;
        }, this.acc);
      }
      else {
        ((DisposableBean) this.bean).destroy();
      }
    }
    catch (Throwable ex) {
      String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
      if (logger.isDebugEnabled()) {
        logger.warn(msg, ex);
      }
      else {
        logger.warn(msg + ": " + ex);
      }
    }
  }
// 如果destroyMethod 不为null,则触发自定义销毁方法
  if (this.destroyMethod != null) {
    invokeCustomDestroyMethod(this.destroyMethod);
  }
  else if (this.destroyMethodName != null) {
  // 如果destroyMethodName 不为null,则获取并触发自定义销毁方法
    Method methodToInvoke = determineDestroyMethod(this.destroyMethodName);
    if (methodToInvoke != null) {
      invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke));
    }
  }
}

方法流程梳理如下 :

  • 获取当前实例维护的DestructionAwareBeanPostProcessor 触发其
  • postProcessBeforeDestruction方法,如果有@PreDestroy注解的方法,这时会被触发。
  • 根据invokeDisposableBean判断是否触发destroy方法
  • 尝试触发其自定义destroy method


目录
相关文章
|
7月前
|
Java 容器 Spring
SpringBoot:Bean生命周期自定义初始化和销毁
SpringBoot:Bean生命周期自定义初始化和销毁
192 1
|
Java 容器 Spring
SpringBoot之退出服务(exit)时调用自定义的销毁方法
我们在工作中有时候可能会遇到这样场景,需要在退出容器的时候执行某些操作。SpringBoot中有两种方法可以供我们来选择(其实就是spring中我们常用的方式。只是destory-method是在XML中配置的,SpringBoot是去配置化。所以这里就不提这种方式了),一种是实现DisposableBean接口,一种是使用@PreDestroy注解。OK,下面我写两个例子看一下: D
3427 0
|
3月前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的服装商城管理系统
基于Java+Springboot+Vue开发的服装商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的服装商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
190 2
基于Java+Springboot+Vue开发的服装商城管理系统
|
2月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
179 1
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,创建并配置 Spring Boot 项目,实现后端 API;然后,使用 Ant Design Pro Vue 创建前端项目,配置动态路由和菜单。通过具体案例,展示了如何快速搭建高效、易维护的项目框架。
117 62
|
11天前
|
存储 JavaScript 前端开发
基于 SpringBoot 和 Vue 开发校园点餐订餐外卖跑腿Java源码
一个非常实用的校园外卖系统,基于 SpringBoot 和 Vue 的开发。这一系统源于黑马的外卖案例项目 经过站长的进一步改进和优化,提供了更丰富的功能和更高的可用性。 这个项目的架构设计非常有趣。虽然它采用了SpringBoot和Vue的组合,但并不是一个完全分离的项目。 前端视图通过JS的方式引入了Vue和Element UI,既能利用Vue的快速开发优势,
66 13
|
19天前
|
JavaScript 安全 Java
java版药品不良反应智能监测系统源码,采用SpringBoot、Vue、MySQL技术开发
基于B/S架构,采用Java、SpringBoot、Vue、MySQL等技术自主研发的ADR智能监测系统,适用于三甲医院,支持二次开发。该系统能自动监测全院患者药物不良反应,通过移动端和PC端实时反馈,提升用药安全。系统涵盖规则管理、监测报告、系统管理三大模块,确保精准、高效地处理ADR事件。
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
82 2
|
1月前
|
JavaScript Java 项目管理
Java毕设学习 基于SpringBoot + Vue 的医院管理系统 持续给大家寻找Java毕设学习项目(附源码)
基于SpringBoot + Vue的医院管理系统,涵盖医院、患者、挂号、药物、检查、病床、排班管理和数据分析等功能。开发工具为IDEA和HBuilder X,环境需配置jdk8、Node.js14、MySQL8。文末提供源码下载链接。
|
3月前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的大学竞赛报名管理系统
基于Java+Springboot+Vue开发的大学竞赛报名管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的大学竞赛报名管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
238 3
基于Java+Springboot+Vue开发的大学竞赛报名管理系统