• spring bean 卸载
  • 简单卸载示例
  • 源码分析
  • 逆向-close
  • 逆向-引用搜索
  • 逆向-条件判断
  • bean的卸载方法
  • 问题场景
  • 自动定义


spring bean 卸载

起因:

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

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

  1. 自定义destory-method
  2. 实现 org.springframework.beans.factory.DisposableBean 或者 java.lang.AutoCloseable
  3. @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);
    }

这里就到我们的结论区了

  1. 实现 org.springframework.beans.factory.DisposableBean 或者 java.lang.AutoCloseable
  2. @Bean 注解时,自动推断. 存在close() 或者 shutdown() 就调用
  3. 自定义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();
        }

    }