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


目录
相关文章
|
6月前
|
Java 容器 Spring
SpringBoot:Bean生命周期自定义初始化和销毁
SpringBoot:Bean生命周期自定义初始化和销毁
173 1
|
Java 容器 Spring
SpringBoot之退出服务(exit)时调用自定义的销毁方法
我们在工作中有时候可能会遇到这样场景,需要在退出容器的时候执行某些操作。SpringBoot中有两种方法可以供我们来选择(其实就是spring中我们常用的方式。只是destory-method是在XML中配置的,SpringBoot是去配置化。所以这里就不提这种方式了),一种是实现DisposableBean接口,一种是使用@PreDestroy注解。OK,下面我写两个例子看一下: D
3415 0
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
144 1
|
2月前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的服装商城管理系统
基于Java+Springboot+Vue开发的服装商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的服装商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
152 2
基于Java+Springboot+Vue开发的服装商城管理系统
|
2月前
|
前端开发 JavaScript Java
SpringBoot项目部署打包好的React、Vue项目刷新报错404
本文讨论了在SpringBoot项目中部署React或Vue打包好的前端项目时,刷新页面导致404错误的问题,并提供了两种解决方案:一是在SpringBoot启动类中配置错误页面重定向到index.html,二是将前端路由改为hash模式以避免刷新问题。
230 1
|
18天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,创建并配置 Spring Boot 项目,实现后端 API;然后,使用 Ant Design Pro Vue 创建前端项目,配置动态路由和菜单。通过具体案例,展示了如何快速搭建高效、易维护的项目框架。
95 62
|
16天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
34 2
|
18天前
|
JavaScript Java 项目管理
Java毕设学习 基于SpringBoot + Vue 的医院管理系统 持续给大家寻找Java毕设学习项目(附源码)
基于SpringBoot + Vue的医院管理系统,涵盖医院、患者、挂号、药物、检查、病床、排班管理和数据分析等功能。开发工具为IDEA和HBuilder X,环境需配置jdk8、Node.js14、MySQL8。文末提供源码下载链接。
|
2月前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的大学竞赛报名管理系统
基于Java+Springboot+Vue开发的大学竞赛报名管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的大学竞赛报名管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
218 3
基于Java+Springboot+Vue开发的大学竞赛报名管理系统
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用
【10月更文挑战第8天】本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,通过 Spring Initializr 创建并配置 Spring Boot 项目,实现后端 API 和安全配置。接着,使用 Ant Design Pro Vue 脚手架创建前端项目,配置动态路由和菜单,并创建相应的页面组件。最后,通过具体实践心得,分享了版本兼容性、安全性、性能调优等注意事项,帮助读者快速搭建高效且易维护的应用框架。
41 3