Spring源码解析之bean的销毁

本文详细介绍了Spring框架中Bean的销毁过程,包括销毁Bean的几种常见场景、相关方法的调用流程以及Bean销毁的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://blog.youkuaiyun.com/heroqiang/article/details/81541208

                    版权声明:本文为博主原创文章,转载请注明出处。                        https://blog.youkuaiyun.com/heroqiang/article/details/81541208                    </div>
                                                <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-3019150162.css">
                                    <div id="content_views" class="markdown_views prism-atom-one-light">
                <!-- flowchart 箭头图标 勿删 -->
                <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
                    <path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path>
                </svg>
                                        <h3><a name="t0"></a><a id="_0"></a>阅读须知</h3>
  • Spring源码版本:4.3.8
  • 文章中使用/* */注释的方法会做深入分析

正文

我们来看几个销毁bean的场景,在一些异常情况,例如Spring上下文初始化失败时,会销毁已经创建的单例bean,这时会调用相关销毁方法,在Spring容器关闭时,同样会调用disposableBeans的销毁方法:
AbstractApplicationContext:

public void registerShutdownHook() {
    if (this.shutdownHook == null) {
        this.shutdownHook = new Thread() {
            @Override
            public void run() {
                synchronized (startupShutdownMonitor) {
                    /* 关闭容器 */
                    doClose();
                }
            }
        };
        Runtime.getRuntime().addShutdownHook(this.shutdownHook);
    }
}

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

AbstractApplicationContext:

public void close() {
    synchronized (this.startupShutdownMonitor) {
        /* 关闭容器 */
        doClose();
        if (this.shutdownHook != null) {
            try {
                Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
            }
            catch (IllegalStateException ex) {
            }
        }
    }
}

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在容器注册的关闭钩子的关闭方法中都调用了doClose方法,我们来分析这个方法:

protected void doClose() {
    // 判断active激活标记(在初始化上下文时被设置为true用于标记激活状态)并且将closed标记设置为true
    if (this.active.get() && this.closed.compareAndSet(false, true)) {
        if (logger.isInfoEnabled()) {
            logger.info("Closing " + this);
        }
        // 卸载注册的JMX的MBean
        LiveBeansView.unregisterApplicationContext(this);
        try {
            // 发布容器关闭事件
            publishEvent(new ContextClosedEvent(this));
        }
        catch (Throwable ex) {
            logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
        }
        try {
            // 调用实现了Lifecycle的bean的stop方法,关于Lifecycle,我们在标签解析的文章中分析过
            getLifecycleProcessor().onClose();
        }
        catch (Throwable ex) {
            logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
        }
        /* 销毁bean */
        destroyBeans();
        // 关闭BeanFactory,将BeanFactory序列化id和本身置为null
        closeBeanFactory();
        // 子类扩展
        onClose();
        // 将激活标记置为false
        this.active.set(false);
    }
}

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

AbstractApplicationContext:

protected void destroyBeans() {
    // 销毁单例bean
    getBeanFactory().destroySingletons();
}

 
  • 1
  • 2
  • 3
  • 4

DefaultListableBeanFactory:

public void destroySingletons() {
    /* 调用父类的销毁方法销毁单例bean */
    super.destroySingletons();
    // 清空手工注册的beanName的缓存
    this.manualSingletonNames.clear();
    // 清空类型-->beanName的映射缓存
    clearByTypeCache();
}

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

DefaultSingletonBeanRegistry:

public void destroySingletons() {
    if (logger.isDebugEnabled()) {
        logger.debug("Destroying singletons in " + this);
    }
    synchronized (this.singletonObjects) {
        // 设置正在销毁的标记为true
        this.singletonsCurrentlyInDestruction = true;
    }
    String[] disposableBeanNames;
    synchronized (this.disposableBeans) {
        disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
    }
    for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
        /* 遍历销毁之前注册的所有disposableBean */
        destroySingleton(disposableBeanNames[i]);
    }
    // 清空beanName --> 它包含的所有内部beanName集合的映射缓存
    this.containedBeanMap.clear();
    // 清空beanName --> 它依赖的所有beanName集合的映射缓存
    this.dependentBeanMap.clear();
    // 清空beanName --> 依赖它的所有beanName集合的映射缓存
    this.dependenciesForBeanMap.clear();
    synchronized (this.singletonObjects) {
        // 清空单例bean缓存
        this.singletonObjects.clear();
        // 清空单例工厂缓存
        this.singletonFactories.clear();
        // 清空提前暴露的beanName --> bean的映射缓存
        this.earlySingletonObjects.clear();
        // 清空已经注册的单例bean缓存
        this.registeredSingletons.clear();
        // 设置正在销毁的标记为false
        this.singletonsCurrentlyInDestruction = false;
    }
}

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

DefaultSingletonBeanRegistry:

public void destroySingleton(String beanName) {
    // 清除bean的相应缓存
    removeSingleton(beanName);
    DisposableBean disposableBean;
    synchronized (this.disposableBeans) {
        // 移除并获取disposableBean
        disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
    }
    /* 销毁 */
    destroyBean(beanName, disposableBean);
}

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

DefaultSingletonBeanRegistry:

protected void destroyBean(String beanName, DisposableBean bean) {
    Set<String> dependencies = this.dependentBeanMap.remove(beanName);
    if (dependencies != null) {
        if (logger.isDebugEnabled()) {
            logger.debug("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
        }
        for (String dependentBeanName : dependencies) {
            // 首选递归销毁所有当前bean依赖的bean
            destroySingleton(dependentBeanName);
        }
    }
    if (bean != null) {
        try {
            // 调用destory方法
            bean.destroy();
        }
        catch (Throwable ex) {
            logger.error("Destroy method on bean with name '" + beanName + "' threw an exception", ex);
        }
    }
    Set<String> containedBeans = this.containedBeanMap.remove(beanName);
    if (containedBeans != null) {
        for (String containedBeanName : containedBeans) {
            // 递归销毁当前bean包含的所有内部bean
            destroySingleton(containedBeanName);
        }
    }
    synchronized (this.dependentBeanMap) {
        // 遍历找出所有依赖当前bean的列表,将当前bean从被依赖的列表中移除
        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();
            }
        }
    }
    // 从所有依赖当前bean的映射中移除依赖关系
    this.dependenciesForBeanMap.remove(beanName);
}

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

整个Spring销毁bean的流程到这里就结束了。

                                </div>
            <link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-e44c3c0e64.css" rel="stylesheet">
                </div>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hello_world!

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值