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

}

【转】spring bean 卸载的更多相关文章

  1. Spring8:一些常用的Spring Bean扩展接口

    前言 Spring是一款非常强大的框架,可以说是几乎所有的企业级Java项目使用了Spring,而Bean又是Spring框架的核心. Spring框架运用了非常多的设计模式,从整体上看,它的设计严格 ...

  2. Spring Bean详细讲解

    什么是Bean? Spring Bean是被实例的,组装的及被Spring 容器管理的Java对象. Spring 容器会自动完成@bean对象的实例化. 创建应用对象之间的协作关系的行为称为:装配( ...

  3. Spring Bean的生命周期(非常详细)

    Spring作为当前Java最流行.最强大的轻量级框架,受到了程序员的热烈欢迎.准确的了解Spring Bean的生命周期是非常必要的.我们通常使用ApplicationContext作为Spring ...

  4. spring bean的生命周期

    掌握好spring bean的生命周期,对spring的扩展大有帮助.  spring bean的生命周期(推荐看)  spring bean的生命周期

  5. spring bean的重新加载

    架构体系 在谈spring bean的重新加载前,首先我们来看看spring ioc容器. spring ioc容器主要功能是完成对bean的创建.依赖注入和管理等功能,而这些功能的实现是有下面几个组 ...

  6. Spring Bean

    一.Spring的几大模块:Data access & Integration.Transcation.Instrumentation.Core Spring Container.Testin ...

  7. 【转】Spring bean处理——回调函数

    Spring bean处理——回调函数 Spring中定义了三个可以用来对Spring bean或生成bean的BeanFactory进行处理的接口,InitializingBean.BeanPost ...

  8. 在非spring组件中注入spring bean

    1.在spring中配置如下<context:spring-configured/>     <context:load-time-weaver aspectj-weaving=&q ...

  9. spring bean生命周期管理--转

    Life Cycle Management of a Spring Bean 原文地址:http://javabeat.net/life-cycle-management-of-a-spring-be ...

随机推荐

  1. Spring MVC -- MVC设计模式(演示4个基于MVC框架的案例)

    对于简单的Java Web项目,我们的项目仅仅包含几个jsp页面,由于项目比较小,我们通常可以通过链接方式进行jsp页面间的跳转. 但是如果是一个中型或者大型的项目,上面那种方式就会带来许多维护困难, ...

  2. 【miscellaneous】Winserver2012安装后无法进入桌面

    原因分析: 自己操作卸载了.net framework,系统没有了图形界面(由完整模式Full变为了核心模式core),需要重新恢复.net framework4.5. 解决方法分析: 需要将核心模式 ...

  3. 【Linux开发】【CUDA开发】Ubuntu上安装NVIDIA显卡驱动

    机型为戴尔Vostro3900  显卡型号为GTX 745  对于Nvidia显卡的驱动,如今很多Linux发行版会默认使用名为nouveau的驱动程序.Nouveau是由第三方为Nvidia开发的一 ...

  4. Deepin15.11源码安装Nginx17.5包括stream模块和njs模块

    一:先到官网下载nginx-1.17.5.tar.gz包并且解压到当前目录,解压后目录为:nginx-1.17.5: 二:下载njs源码(它没有像stream模块一样附带在了nginx源码里),因此首 ...

  5. 15 Filter过滤器和Listener监听器

    1.Filter:过滤器 (1) 概念:生活中的过滤器:净水器,空气净化器,土匪.web中的过滤器:当访问服务器的资源时,过滤器可以将请求拦截下来,完成一些特殊的功能.过滤器的作用:一般用于完成通用的 ...

  6. [转帖]B树索引和位图索引的结构介绍

    B树索引和位图索引的结构介绍 http://blog.itpub.net/12679300/viewspace-1174236/ 一  前言:? ROWID:包含键值的行的行ID,(查找块的最快方法, ...

  7. Windows 下升级 node & npm 到最新版本

    查询 Node 的安装目录where node 升级 Node:在官网下载最新的安装包,直接安装即可.https://nodejs.org/ 升级 npmnpm install -g npm 使用 n ...

  8. MySQL 总结篇 [在Oracle基础上学习的MySQL]

    一.学习环境 MySQL 5.7社区版 Navicat for MySQL 百度网盘下载链接:https://pan.baidu.com/s/1yGnznwoZ8CaMgrCsDBhKAQ 二.MyS ...

  9. Keil 5出现Error: L6218E: Undefined symbol解决方法

    首先列出网上百度到比较好的blog: blog1:https://blog.csdn.net/super_demo/article/details/32131379 总结了代码中可能因为几种初级或者粗 ...

  10. 隐藏GridControl的“Drag a column header here to group by that column”

    打开设计器,找到OptionsView,往下拉设置showGroupPanel为false