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();

}

}