Spring Bean的资源释放是如何被调用的

362 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第22天,点击查看活动详情

前言

我们在上一篇文章中讲述了如何设置Spring Bean对象的资源释放方法,那么有一个疑问就是,这些配置好的destory-method是如何被调用的呢?

为了解开这个疑惑,我们不得不深入Spring的源码来分析一下;

容器关闭

Spring Bean之所以被销毁,其实与Spring容器本身被关闭有关系,我们可以看到在Spring容器调用close()方法时,就会触发Bean的销毁动作,可以查看AbstractApplicationContext.close()方法:

   protected void doClose() {
       // 容器还是启动状态并且没有被关闭的情况下才能执行下面的代码
       if (this.active.get() && this.closed.compareAndSet(falsetrue)) {
           if (this.logger.isDebugEnabled()) {
               this.logger.debug("Closing " + this);
          }
​
           if (!NativeDetector.inNativeImage()) {
               LiveBeansView.unregisterApplicationContext(this);
          }
           // 发布ContextClosedEvent事件
           try {
               this.publishEvent((ApplicationEvent)(new ContextClosedEvent(this)));
          } catch (Throwable var3) {
               this.logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", var3);
          }
           // 关闭lifecycle bean
           if (this.lifecycleProcessor != null) {
               try {
                   this.lifecycleProcessor.onClose();
              } catch (Throwable var2) {
                   this.logger.warn("Exception thrown from LifecycleProcessor on context close", var2);
              }
          }
           // 执行Bean配置的销毁方法
           this.destroyBeans();
           // 关闭BeanFactory
           this.closeBeanFactory();
           this.onClose();
           // 处理earlyApplicationListeners监听器列表
           if (this.earlyApplicationListeners != null) {
               this.applicationListeners.clear();
               this.applicationListeners.addAll(this.earlyApplicationListeners);
          }
           // 设置为非活跃状态
           this.active.set(false);
      }
​
  }

Spring容器执行close()时,大概会做以下几件事情:

1.发布一个ContextClosedEvent事件,表示Spring容器即将要关闭了;

2.处理lifecycle bean,这些Bean只在容器启动和关闭的时候会被调用相关的方法;

3.执行Bean的销毁方法,也就是destory-methodxml配置的destory-method@PreDestory注解标注的方法以及实现了DisposableBean接口的destory()方法,都属于要被执行的方法;

4.关闭BeanFactory

5.处理earlyApplicationListeners监听器列表;

6.修改Spring容器运行状态;

我们最主要需要关心的就是第三步,它的原理就是把所有实现了DisposableBean接口的Bean都取出来执行一遍destory()方法:

   protected void destroyBeans() {
       this.getBeanFactory().destroySingletons();
  }
​
   public void destroySingletons() {
       if (this.logger.isTraceEnabled()) {
           this.logger.trace("Destroying singletons in " + this);
      }
​
       synchronized(this.singletonObjects) {
           this.singletonsCurrentlyInDestruction = true;
      }
​
     // 取出所有实现DisposableBean接口的Bean名称
       String[] disposableBeanNames;
       synchronized(this.disposableBeans) {
           disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
      }
       // 依次调用destory()方法
       for(int i = disposableBeanNames.length - 1; i >= 0; --i) {
           this.destroySingleton(disposableBeanNames[i]);
      }
​
       this.containedBeanMap.clear();
       this.dependentBeanMap.clear();
       this.dependenciesForBeanMap.clear();
       this.clearSingletonCache();
  }

到这里就已经很明朗了,Spring容器在销毁前会把所有实现了DisposableBean接口的Bean都执行一下destory()方法,这样就实现了Bean的资源释放的功能了;

销毁方法的注入

按照上面的源码分析,Spring容器销毁前只会执行DisposableBean的销毁方法,那么通过xml配置文件或@PreDestory注解设置的销毁方法是不是就无效了呢?

Spring Bean的生命周期中,我们可以在看到AbstractAutowireCapableBeanFactory.doCreateBean()方法的最后一步调用了registerDisposableBeanIfNecessary()方法:

try {
   // 注册销毁方法
   this.registerDisposableBeanIfNecessary(beanName, bean, mbd);
   return exposedObject;
} catch (BeanDefinitionValidationException var16) {
   throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", var16);
}

我们来看看里面做了啥:

  protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
       AccessControlContext acc = System.getSecurityManager() != null ? this.getAccessControlContext() : null;
       // requiresDestruction()方法检查是否指定销毁方法
       if (!mbd.isPrototype() && this.requiresDestruction(bean, mbd)) {
           if (mbd.isSingleton()) {
               // 注册销毁方法,通过DisposableBeanAdapter做了一层包装
               this.registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, mbd, this.getBeanPostProcessorCache().destructionAware, acc));
          } else {
               Scope scope = (Scope)this.scopes.get(mbd.getScope());
               if (scope == null) {
                   throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
              }
             // 和上面一样通过DisposableBeanAdapter包装
               scope.registerDestructionCallback(beanName, new DisposableBeanAdapter(bean, beanName, mbd, this.getBeanPostProcessorCache().destructionAware, acc));
          }
      }
​
  }

this.requiresDestruction(bean, mbd)方法可以解开我们的疑惑:

   protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {
       return bean.getClass() != NullBean.class && 
         // 必须有销毁方法,比如实现了DisposableBean接口,或者BeanDefinition配置了destory-method,或者实现了AutoCloseable接口的close()方法
        (DisposableBeanAdapter.hasDestroyMethod(bean, mbd) || 
          // 或者被注解@PreDestory注解标注了方法
          // 被@PreDestory注解标注的原理可以查看CommonAnnotationBeanPostProcessor类
          this.hasDestructionAwareBeanPostProcessors() && DisposableBeanAdapter.hasApplicableProcessors(bean, this.getBeanPostProcessorCache().destructionAware));
  }

也就是说,所有包含销毁方法的bean都会被包装成DisposableBeanAdapter,因为DisposableBeanAdapter实现了DisposableBean接口,所以在最后Spring容器关闭时只需要统一调用DisposableBean接口的destory()方法即可;