# 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 也一起销毁。**


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ldbmcs.gitbook.io/java/java-frameworks-71/spring/spring-ioc/spring-bean-de-sheng-ming-zhou-qi-wu-xiao-hui-jie-duan.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
