• 【转】spring bean 卸载


    spring bean 卸载
    起因:


    群里的一个朋友问到: 关于配置destory-method, springboot中 yml如何指定

    首先介绍 bean卸载的三种形式

    自定义destory-method
    实现 org.springframework.beans.factory.DisposableBean 或者 java.lang.AutoCloseable
    @Bean 注解时,自动推断. 存在close() 或者 shutdown() 就调用
    接下来看一个简单的卸载bean的例子

    简单卸载示例
    bean实体

    public class MyDispose implements Closeable {
    @Override
    public void close() throws IOException {
    System.out.println("MyDispose 执行关闭");
    }
    }

    applicationContext.xml

    <bean name="disposeBean" class="com.aya.mapper.model.MyDispose">
    </bean>

    测试类

    @Test
    public void testApplicationContextGetBean() {
    ClassPathXmlApplicationContext factory = new ClassPathXmlApplicationContext("applicationContext.xml");
    factory.close();
    }

    控制台输出:MyDispose 执行关闭

    源码分析
    接下来逆向分析,找到spring是如何卸载bean的

    逆向-close
    在 MyDispose.close() 断点

    顺着调用堆栈一层一层往上找,直到 destroySingleton 部分

    org.springframework.beans.factory.support.DefaultSingletonBeanRegistry

    public void destroySingleton(String beanName) {
    // Remove a registered singleton of the given name, if any.
    removeSingleton(beanName);

    // Destroy the corresponding DisposableBean instance.
    DisposableBean disposableBean;
    synchronized (this.disposableBeans) {
    //集合移除对象,返回被移除的对象
    disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
    }
    // 卸载移除的对象
    destroyBean(beanName, disposableBean);
    }

    得出结论: this.disposableBeans.put 的地方就是注册卸载bean的地方,那里一定有条件判断

    逆向-引用搜索
    找到 this.disposableBeans 的定义private final Map<String, Object> disposableBeans = new LinkedHashMap<>();

    然后搜索 disposableBeans 的所有引用,找到disposableBeans.put 的代码区

    public void registerDisposableBean(String beanName, DisposableBean bean) {
    synchronized (this.disposableBeans) {
    this.disposableBeans.put(beanName, bean);
    }
    }

    接下来对 registerDisposableBean 断点,在按照同样的方式,栈针回溯

    逆向-条件判断
    org.springframework.beans.factory.support.DefaultSingletonBeanRegistry

    protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
    AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
    // bean的 scope!=prototype && 必须是销毁的bean
    if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
    if (mbd.isSingleton()) {
    // 将 beanName 添加到 this.disposableBeans
    registerDisposableBean(beanName,
    new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
    }
    else {
    // A bean with a custom scope...
    Scope scope = this.scopes.get(mbd.getScope());
    if (scope == null) {
    throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
    }
    scope.registerDestructionCallback(beanName,
    new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
    }
    }
    }

    接下来跟踪条件判断requiresDestruction,分析卸载的源头

    org.springframework.beans.factory.support.DefaultSingletonBeanRegistry

    protected boolean requiresDestruction(@Nullable Object bean, RootBeanDefinition mbd) {

    return (bean != null &&
    (DisposableBeanAdapter.hasDestroyMethod(bean, mbd) || (hasDestructionAwareBeanPostProcessors() &&
    DisposableBeanAdapter.hasApplicableProcessors(bean, getBeanPostProcessors()))));
    }


    分为3组条件
    1. bean!=null
    2. bean 有卸载方法
    3. 有实现DestructionAwareBeanPostProcessor的bean 并且 方法DestructionAwareBeanPostProcessor.requiresDestruction(bean)的结果为true

    bean的卸载方法
    org.springframework.beans.factory.support.DisposableBeanAdapter

    private static final String CLOSE_METHOD_NAME = "close";

    private static final String SHUTDOWN_METHOD_NAME = "shutdown";

    public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {
    //实现 `org.springframework.beans.factory.DisposableBean` 或者 `java.lang.AutoCloseable`
    if (bean instanceof DisposableBean || bean instanceof AutoCloseable) {
    return true;
    }
    String destroyMethodName = beanDefinition.getDestroyMethodName();
    //@Bean 定义的bean. BeanDefinition的 destoryMethodName=AbstractBeanDefinition.INFER_METHOD
    if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName)) {

    return (ClassUtils.hasMethod(bean.getClass(), CLOSE_METHOD_NAME) ||
    ClassUtils.hasMethod(bean.getClass(), SHUTDOWN_METHOD_NAME));
    }
    //自定义destory-method
    return StringUtils.hasLength(destroyMethodName);
    }

    这里就到我们的结论区了

    实现 org.springframework.beans.factory.DisposableBean 或者 java.lang.AutoCloseable
    @Bean 注解时,自动推断. 存在close() 或者 shutdown() 就调用
    自定义destory-method
    问题场景
    org.apache.commons.dbcp.BasicDataSource 为什么在xml必须定义destory-method而yml不用呢?

    xml里面, BeanDefinition的destoryMethodName属性默认为null.

    yml里面, 通过@Bean定义的bean, BeanDefinition的destoryMethodName属性默认为(inferred).

    也就是说xml必须手动指定, @Bean 就算没指定,也会推断有没有close()或者shutdown方法,有就调用

    自动定义
    关于springboot中自动定义 DataSource 的相关内容做一个简单的描述

    存在 spring.datasource.type 时, 注册相关的Bean

    @ConditionalOnMissingBean(DataSource.class)
    @ConditionalOnProperty(name = "spring.datasource.type")
    static class Generic {

    @Bean
    public DataSource dataSource(DataSourceProperties properties) {
    return properties.initializeDataSourceBuilder().build();
    }

    }

  • 相关阅读:
    P1886 滑动窗口 单调队列
    用三维的视角理解二维世界:完美解释meshgrid函数,三维曲面,等高线,看完你就懂了。...
    用三维的视角理解二维世界:完美解释meshgrid函数,三维曲面,等高线,看完你就懂了。...
    SaltStack入门
    编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
    time模块中time.time与time.sleep
    【函数篇】装饰器
    【函数篇】函数的进阶,名称空间、作用域、函数的嵌套、作用域链
    闭包!!!
    默认参数的陷阱
  • 原文地址:https://www.cnblogs.com/exmyth/p/11668960.html
Copyright © 2020-2023  润新知