# Spring Bean的生命周期(五) - 销毁阶段

bean 完整生命周期原理的最后一部分，我们来研究应用停止时，bean 的销毁阶段都有哪些动作和过程。

本章主要会涉及到的原理部分：

* bean 销毁前 IOC 容器的处理
* bean 销毁阶段的回调

在测试代码中，我们有主动调用 `ApplicationContext` 的 `stop` 和 `close` 方法，这样 IOC 容器的关闭和销毁流程就会依次执行了。

```java
    ctx.stop();
    System.out.println("================IOC容器停止成功==================");
    ctx.close();
}
```

先来看 `stop` 的内容。

## 1. ApplicationContext#stop

```java
public void stop() {
    getLifecycleProcessor().stop();
    publishEvent(new ContextStoppedEvent(this));
}
```

呦，这个套路不正好跟上一章看到的 `start` 动作完全相反吗？这个 `LifecycleProcessor` 中的 `stop` 方法一定也是先给所有的 `Lifecycle` bean 分组，排序，依次调用 `stop` 方法。当然，我们的推测是绝对正确的，小伙伴们可以跟着源码进去看一看，小册就不贴出来了。

## 2. ApplicationContext#close

```java
public void close() {
    synchronized (this.startupShutdownMonitor) {
        doClose();
        if (this.shutdownHook != null) {
            try {
                Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
            }
            // catch ......
        }
    }
}
```

关闭容器的动作，仅仅只有一个 `doClose` 方法，那我们就往里进就好。不过这个 `doClose` 方法有点长，我们拆解开来讲解。

### 2.1 广播事件

```java
protected void doClose() {
    // Check whether an actual close attempt is necessary...
    if (this.active.get() && this.closed.compareAndSet(false, true)) {
        // logger ......
        LiveBeansView.unregisterApplicationContext(this);
        try {
            // Publish shutdown event.
            publishEvent(new ContextClosedEvent(this));
        } // catch ......
        // ......
```

这一段非常简单，广播 `ContextClosedEvent` 事件而已。前面在事件章节的讲解中我们也说过，SpringFramework 中一共内置了 4 个可供监听的预设事件，它们**都是给开发者自己使用**的，SpringFramework 内部是没有任何利用的。

### 2.2 LifecycleProcessor#onClose

```java
        // ......
        // Stop all Lifecycle beans, to avoid delays during individual destruction.
        if (this.lifecycleProcessor != null) {
            try {
                this.lifecycleProcessor.onClose();
            } // catch ......
        }
        // ......
```

等一下？这怎么又是 `LifecycleProcessor` ？该不会。。。它的 `onClose` 方法又是跟 `stop` 一样？看一眼源码：

```java
public void onClose() {
    stopBeans();
    this.running = false;
}
```

wtf ？？？好吧，果然跟上面是一样一样的，那小册就不赘述啦。

### 2.3 destroyBeans - 销毁bean

```java
// ......
// Destroy all cached singletons in the context's BeanFactory.
destroyBeans();
// ......
```

这一部分我们只看这一个方法，不要觉得它简单哦，它的复杂度还是挺高的，我们进入来看。

```java
protected void destroyBeans() {
    getBeanFactory().destroySingletons();
}
```

实现倒也简单，仅仅是获取到 `ApplicationContext` 内部的 `BeanFactory` ，让它去销毁所有的单实例 bean 。

#### 2.3.1 DefaultListableBeanFactory#destroySingletons

继续往里走，进入到 `DefaultListableBeanFactory` 中：

```java
public void destroySingletons() {
    super.destroySingletons();
    // 清空单实例bean的名称
    updateManualSingletonNames(Set::clear, set -> !set.isEmpty());
    clearByTypeCache();
}

private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<>(64);
private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<>(64);
private void clearByTypeCache() {
    this.allBeanNamesByType.clear();
    this.singletonBeanNamesByType.clear();
}
```

这段代码中首先会调用父类 `DefaultSingletonBeanRegistry` 的 `destroySingletons` 方法，随后会清空所有的单实例 bean 的名称，以及 “类型到 name ” 的映射。（ `allBeanNamesByType` 中保存了 bean 的类型包含的所有 bean ，如 `Person` 类型的 bean 在 IOC 容器中包含 `master` 和 `admin` ）

#### 2.3.2 DefaultSingletonBeanRegistry#destroySingletons

重点还是在 `DefaultSingletonBeanRegistry` 中，进入它的 `destroySingletons` 方法实现：

```java
public void destroySingletons() {
    // logger ......
    synchronized (this.singletonObjects) {
        this.singletonsCurrentlyInDestruction = true;
    }

    String[] disposableBeanNames;
    synchronized (this.disposableBeans) {
        // set -> array
        disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
    }
    // 销毁所有单实例bean
    for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
        destroySingleton(disposableBeanNames[i]);
    }

    // 清空一切缓存
    this.containedBeanMap.clear();
    this.dependentBeanMap.clear();
    this.dependenciesForBeanMap.clear();

    clearSingletonCache();
}
```

这段源码看着挺长，核心就一句：重载的 `destroySingleton(beanName)` 。下面的好多 `Map` 的 `clear` 动作，以及 `clearSingletonCache` 方法，都是清除掉缓存而已，核心还是逐个销毁单实例 bean 。

#### 2.3.3 destroySingleton

进入到单个 bean 的销毁流程，Debug 发现又回到 `DefaultListableBeanFactory` 了：

```java
public void destroySingleton(String beanName) {
    super.destroySingleton(beanName);
    removeManualSingletonName(beanName);
    clearByTypeCache();
}
```

-.- 又来这一套是吧，合着销毁一个，跟销毁一堆的套路是一样一样的，那我们继续往里进，还是走到 `DefaultSingletonBeanRegistry` 中：

```java
public void destroySingleton(String beanName) {
    // Remove a registered singleton of the given name, if any.
    // 此处会清理掉BeanFactory中设计的用于处理bean循环依赖的三级缓存
    // 如果小伙伴对此感兴趣，可以参考boot小册第15章
    removeSingleton(beanName);

    // Destroy the corresponding DisposableBean instance.
    DisposableBean disposableBean;
    synchronized (this.disposableBeans) {
        disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
    }
    destroyBean(beanName, disposableBean);
}
```

销毁单个 bean 的动作总共就两步：**清空** `**BeanFactory**` **中用于处理循环依赖的缓存，回调 bean 的销毁动作**。重要的方法仍然在最后，我们继续往里深入：

#### 2.3.4 destroyBean

这个方法又是好长啊，我们还是拆解开来看。

**销毁依赖的bean**

```java
protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
    Set<String> dependencies;
    synchronized (this.dependentBeanMap) {
        dependencies = this.dependentBeanMap.remove(beanName);
    }
    if (dependencies != null) {
        // logger ......
        for (String dependentBeanName : dependencies) {
            destroySingleton(dependentBeanName);
        }
    }
    // ......
}
```

首先，在销毁一个 bean 时，如果它有依赖其它的 bean ，则首要目标不是销毁自己，而是先销毁那些依赖的 bean ，所以这里会有递归调用上面 `destroySingleton` 方法的动作。

**自定义bean销毁方法的回调**

```java
    // ......
    // Actually destroy the bean now...
    if (bean != null) {
        try {
            bean.destroy();
        }
        // catch ......
    }
    // ......
```

动作很简单，一个 `destroy` 方法就完事了，点击去看一下吧：

```java
public void destroy() {
    if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
        // 回调DestructionAwareBeanPostProcessor
        // 此处会有执行@PreDestroy注解标注的销毁方法
        for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
            processor.postProcessBeforeDestruction(this.bean, this.beanName);
        }
    }

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

    // 回调自定义的destroy-method方法
    if (this.destroyMethod != null) {
        invokeCustomDestroyMethod(this.destroyMethod);
    }
    else if (this.destroyMethodName != null) {
        Method methodToInvoke = determineDestroyMethod(this.destroyMethodName);
        if (methodToInvoke != null) {
            invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke));
        }
    }
}
```

到这里我们就发现了，它会把我们常用的三种 bean 的销毁手段都依次执行一遍，同时也就解释了 bean 的销毁阶段生命周期回调的顺序：`**@PreDestroy**` **→** `**DisposableBean**` **→** `**destroy-method**` 。

**处理bean中bean**

```java
    // ......
    // Trigger destruction of contained beans...
    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);
        }
    }
    // ......
```

这个概念不是很好理解吧，bean 中 bean 是什么鬼？还记得第 9 章中，我们在写复杂类型注入的时候，说可以在 property 中再写 `<bean>` 这回事吧，不记得也没有关系，我改一下原有的测试代码，想必你一下子就能想起来了吧：

```xml
<bean id="cat" class="com.linkedbear.spring.lifecycle.e_source.bean.Cat">
    <property name="name" value="mimi"/>
    <property name="master">
        <bean class="com.linkedbear.spring.lifecycle.e_source.bean.Person"/>
    </property>
</bean>
```

哎，这种嵌套 bean ，就会记录在 `DefaultSingletonBeanRegistry` 的 `containedBeanMap` 中，既然外头的 bean 要销毁了，那里头的这些 bean 也就应该被销毁了，所以这里又是取到那些内部构造好的 bean ，依次递归调用 `destroySingleton` 方法来销毁这些 bean 。

**销毁被依赖的bean**

```java
    // ......
    // Remove destroyed bean from other beans' dependencies.
    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);
}
```

注意这里的说法，这里是销毁那些**被**依赖的 bean ，由于一个 bean 可能被其它 bean 引用或者依赖，所以在最后一步，**当自己销毁之后，那些依赖了当前 bean 的 bean 也就应该被销毁**。套路都是一样的，取到那些被依赖的 bean 的名称，依次递归调用 `destroySingleton` 方法销毁。

到这里，`destroyBeans` 销毁单实例 bean 的逻辑就全部走完了，继续往下看 `doClose` 方法还干了什么事。

### 2.4 关闭BeanFactory

```java
// Close the state of this context itself.
closeBeanFactory();
```

这个步骤，说是关闭 `BeanFactory` ，实际上更接近于 “销毁” ，因为原来的 `BeanFactory` 无论如何都无法继续用了。

在基于 xml 和基于注解驱动的两种 `ApplicationContext` 的实现里，它们的策略既相似又不同：

```java
// AbstractRefreshableApplicationContext
protected final void closeBeanFactory() {
    DefaultListableBeanFactory beanFactory = this.beanFactory;
    if (beanFactory != null) {
        beanFactory.setSerializationId(null);
        this.beanFactory = null;
    }
}
```

在基于 xml 配置文件的 `ApplicationContext` 中，它会获取到原有的 `BeanFactory` ，移除序列化 ID ，并直接丢弃原来的 `BeanFactory` 。

```java
// GenericApplicationContext
protected final void closeBeanFactory() {
    this.beanFactory.setSerializationId(null);
}
```

在基于注解驱动的 `ApplicationContext` 中，它只会给内部组合的 `BeanFactory` 移除序列化 ID 而已。

#### 2.4.1 GenericApplicationContext不允许被重复刷新的原因

这个时候可能会有小伙伴们产生疑惑：之前不是说 `GenericApplicationContext` 不允许重复刷新吗？既然它只是移除了一下序列化 ID ，那刷新的时候重新设置一个不就行了吗？哎，这个时候我们要了解 `GenericApplicationContext` 中的另外一个成员了：

```java
private final AtomicBoolean refreshed = new AtomicBoolean();

protected final void refreshBeanFactory() throws IllegalStateException {
    if (!this.refreshed.compareAndSet(false, true)) {
        throw new IllegalStateException(
            "GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
    }
}
```

看这里，如果当前 `GenericApplicationContext` 是刚创建的，那么 `refreshed` 的值一定是 false ，此时使用 CAS 设置 true 是成功的，下面的 `throw ex` 动作不会执行；而第二次再调用 `refresh` 刷新 `ApplicationContext` 时，进入到该方法时，CAS 不通过，无法刷新 `BeanFactory` ，最终抛出异常。

为什么 `AbstractRefreshableApplicationContext` 就没这事呢？因为 `AbstractRefreshableApplicationContext` 中本来就没这个 `refreshed` 的设计，而且人家在关闭 `BeanFactory` 的时候，直接把原来的 `BeanFactory` 扔了，不要了，那当然得支持刷新呀（不然 `BeanFactory` 都没了 `ApplicationContext` 就是一空壳）。

### 2.5 剩余动作

```java
    // ......
    // Let subclasses do some final clean-up if they wish...
    // 给子类用的，然而子类没一个重写的
    onClose();

    // Reset local application listeners to pre-refresh state.
    if (this.earlyApplicationListeners != null) {
        this.applicationListeners.clear();
        this.applicationListeners.addAll(this.earlyApplicationListeners);
    }

    // Switch to inactive.
    this.active.set(false);
}
```

剩余的动作就不重要了，而且也没什么实在东西，我们就不关注了。

至此，`ApplicationContext` 关闭，bean 的销毁动作也就全部结束了。

## 3. 总结

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

**销毁动作会先将当前 bean 依赖的所有 bean 都销毁，随后回调自定义的 bean 的销毁方法，之后如果 bean 中有定义内部 bean 则会一并销毁，最后销毁那些依赖了当前 bean 的 bean 也一起销毁。**
