spring bean 卸載
原由:java
羣裏的一個朋友問到: 關於配置destory-method, springboot中 yml如何指定spring
首先介紹 bean卸載的三種形式apache
自定義destory-method
實現 org.springframework.beans.factory.DisposableBean 或者 java.lang.AutoCloseable
@Bean 註解時,自動推斷. 存在close() 或者 shutdown() 就調用
接下來看一個簡單的卸載bean的例子springboot
簡單卸載示例
bean實體app
public class MyDispose implements Closeable {
@Override
public void close() throws IOException {
System.out.println("MyDispose 執行關閉");
}
}
applicationContext.xmlide
<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的ui
逆向-close
在 MyDispose.close() 斷點this
順着調用堆棧一層一層往上找,直到 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();
}
}