目录
- 前言
- 阅读建议
- 课程内容
- 一、Bean什么时候销毁
- 二、实现自定义的Bean销毁逻辑
- 2.1 实现DisposableBean或者AutoCloseable接口
- 2.2 使用@PreDestroy注解
- 2.3 其他方式(手动指定销毁方法名字)
- 三、注册销毁Bean过程及方法详解
- 3.1 AbstractBeanFactory#requiresDestruction:需要销毁吗
- 3.2 DisposableBeanAdapter.hasDestroyMethod:是否有销毁方法
- 3.3 DisposableBeanAdapter#inferDestroyMethodIfNecessary:推断销毁方法
- 3.4 AbstractBeanFactory#hasDestructionAwareBeanPostProcessors:是否有感知销毁Bean后置处理器
- 3.5 DisposableBeanAdapter.hasApplicableProcessors:是否有应用于当前Bean的销毁感知Bean后置处理器
- 3.6 DefaultSingletonBeanRegistry#registerDisposableBean:注册需要销毁的bean
- 3.7 注册销毁Bean过程总结
- 四、注册销毁Bean逻辑流程图
- 学习总结
前言
我们在这里讲的是Bean的销毁过程。也许,不少朋友说到Bean的销毁,可能会想到垃圾回收的东西。虽然都是在做生命周期的最后一部分,但其实这俩不是同一回事。垃圾回收是JVM级别的东西,这里说的Bean销毁是Spring的东西,所以当然不是一回事。
阅读建议
本节课的内容,将会以下面这段代码为入口讲解:
// 注册Bean的销毁接口
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
这段代码,其实是在Spring实例化里面的AbstractAutowireCapableBeanFactory#doCreateBean()
方法里面。而且,通过这个方法名字大家也知道了,这一步仅仅只是注册销毁逻辑而已,并不是真的销毁。只有当一定条件成立的时候,才会去销毁。
registerDisposableBeanIfNecessary
具体代码如下:
/**
* 将给定bean添加到此工厂中的一次性bean列表中,注册其DisposableBean接口和/或给定的destroy方法,以便在工厂关闭时调用(如果适用)。只适用于单例。
* 参数:
* beanName—bean的名称—bean实例mbd—bean的bean定义
* 参见:
* RootBeanDefinition。isSingleton RootBeanDefinition。getDependsOn, registerDisposableBean, registerDependentBean
*/
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
if (mbd.isSingleton()) {
// Register a DisposableBean implementation that performs all destruction
// work for the given bean: DestructionAwareBeanPostProcessors,
// DisposableBean interface, custom destroy method.
registerDisposableBean(beanName, new DisposableBeanAdapter(
bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, 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, getBeanPostProcessorCache().destructionAware, acc));
}
}
}
课程内容
一、Bean什么时候销毁
Bean销毁是发生在Spring容器关闭过程中。这时,Spring所有的单例Bean都会被销毁,并且,会执行各自实现了自定义销毁逻辑的Bean的销毁方法。我们在本篇文章要介绍的,就是:如何实现自定义的Bean销毁逻辑。
在Spring容器关闭时,可以显示关闭,比如:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) context.getBean("userService");
userService.test();
// 容器关闭
context.close();
又或者,注册一个关闭钩子:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 注册关闭钩子
context.registerShutdownHook();
Object newUser = context.getBean("user");
System.out.println(newUser);
【注意:强制杀掉进程(kill pid)是不会调用自定义的Bean销毁逻辑】
Spring关闭容器的过程:
- 首先发布ContextClosedEvent事件
- 调用lifecycleProcessor的onCloese()方法
- 销毁单例Bean
- 遍历disposableBeans
- 把每个disposableBean从单例池中移除
- 调用disposableBean的destroy()
- 如果这个disposableBean还被其他Bean依赖了,那么也得销毁其他Bean
- 如果这个disposableBean还包含了inner beans,将这些Bean从单例池中移除掉
- 清空manualSingletonNames,是一个Set,存的是用户手动注册的单例Bean的beanName
- 清空allBeanNamesByType,是一个Map,key是bean类型,value是该类型所有的beanName数组
- 清空singletonBeanNamesByType,和allBeanNamesByType类似,只不过只存了单例Bean
- 遍历disposableBeans
二、实现自定义的Bean销毁逻辑
实现方式有如下几种:
2.1 实现DisposableBean或者AutoCloseable接口
需要自定义销毁的Bean代码示例:(实现自:DisposableBean )
@Component
public class TestDestroyBean implements DisposableBean {
public void test() {
System.out.println("测试一下销毁方法");
}
@Override
public void destroy() throws Exception {
System.out.println("TestDestroyBean------自定义的Bean销毁方法");
}
}
或者:(实现自:AutoCloseable )
@Component
public class TestDestroyBean implements AutoCloseable {
public void test() {
System.out.println("测试一下销毁方法");
}
@Override
public void close() throws Exception {
System.out.println("TestDestroyBean------自定义的Bean销毁方法");
}
}
2.2 使用@PreDestroy注解
实现方式有如下3种:
@Component
public class TestDestroyBean {
public void test() {
System.out.println("测试一下销毁方法");
}
@PreDestroy
public void close() throws Exception {
System.out.println("TestDestroyBean------自定义的Bean销毁方法");
}
}
2.3 其他方式(手动指定销毁方法名字)
当然,还有其他方式,如:
<bean destroy-method='xxx'>
或者:
@Bean(destroyMethod = "xxx")
又或者就是,在beanDefinition
里面直接指定销毁方法:
@Component
public class MyBeanPostProcessor implements MergedBeanDefinitionPostProcessor {
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
if (beanName.equals("user")) {
beanDefinition.setInitMethodName("myInit");
beanDefinition.setDestroyMethodName("xxxx");
}
}
}
上面说的这三种方式,有一个比较特殊的地方,因为是手动指定的,所以可以设置一个比较特殊的值:(inferred)
。
如果设置了销毁方法名字为这个,并且Bean没有实现DisposableBean
,则,在销毁的过程中,会检索bean下面有没有close
或者shutdown
方法。有,则自动绑定为【用户自定义销毁方法】。
三、注册销毁Bean过程及方法详解
本次销毁过程总过涉及了【3个核心类,6个核心方法】
3.1 AbstractBeanFactory#requiresDestruction:需要销毁吗
方法调用链:从入口:registerDisposableBeanIfNecessary()调用进来
全路径:org.springframework.beans.factory.support.AbstractBeanFactory#requiresDestruction
方法注释:将给定bean添加到此工厂中的一次性bean列表中,注册其DisposableBean接口和/或给定的destroy方法,以便在工厂关闭时调用(如果适用)。只适用于单例。
源码如下:
protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {
return (
bean.getClass() != NullBean.class && (
DisposableBeanAdapter.hasDestroyMethod(bean, mbd)
|| (hasDestructionAwareBeanPostProcessors()
&& DisposableBeanAdapter.hasApplicableProcessors(bean, getBeanPostProcessorCache().destructionAware))
)
);
}
方法解读:里面的关键源码其实就是分两个步骤。如下:
- 是否有指定的销毁方法。
DisposableBeanAdapter.hasDestroyMethod(bean, mbd)
- 是否有
DestructionAwareBeanPostProcessor
,能感知销毁的Bean后置处理器(hasDestructionAwareBeanPostProcessors
)。有则遍历DisposableBeanAdapter.hasApplicableProcessors(bean, getBeanPostProcessorCache().destructionAware)
3.2 DisposableBeanAdapter.hasDestroyMethod:是否有销毁方法
方法调用链:由3.1中的requiresDestruction()调用过来
全路径:org.springframework.beans.factory.support.DisposableBeanAdapter#hasDestroyMethod
方法注释:检查给定bean是否有任何要调用的销毁方法。
源码如下:
public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {
return (bean instanceof DisposableBean || inferDestroyMethodIfNecessary(bean, beanDefinition) != null);
}
而里面的内容其实也很简单,步骤如下:
- 当前Bean是否实现了DisposableBean接口
- 没有,则调用
inferDestroyMethodIfNecessary
推断销毁方法(后面讲)
3.3 DisposableBeanAdapter#inferDestroyMethodIfNecessary:推断销毁方法
方法调用链:由3.2中的hasDestroyMethod()调用过来
全路径:org.springframework.beans.factory.support.DisposableBeanAdapter#inferDestroyMethodIfNecessary
方法注释:
如果给定beanDefinition的"destroyMethodName"属性的当前值是AbstractBeanDefinition。然后尝试推断一个销毁方法。候选方法目前仅限于名为“close”或“shutdown”的公共无参数方法(无论是在本地声明还是继承)。如果没有找到这样的方法,则将给定BeanDefinition的“destroyMethodName”更新为null,否则将设置为推断方法的名称。该常量作为@Bean#destroyMethod属性的默认值,该常量的值也可以在XML中或属性中使用。还处理java.io.Closeable和AutoCloseable接口,并在实现bean时反射地调用“close”方法。
源码如下:
@Nullable
private static String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
String destroyMethodName = beanDefinition.resolvedDestroyMethodName;
if (destroyMethodName == null) {
destroyMethodName = beanDefinition.getDestroyMethodName();
boolean autoCloseable = (bean instanceof AutoCloseable);
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||
(destroyMethodName == null && autoCloseable)) {
// 当销毁方法名字等于"(inferred)",且bean不是DisposableBean实现类
destroyMethodName = null;
if (!(bean instanceof DisposableBean)) {
if (autoCloseable) {
destroyMethodName = CLOSE_METHOD_NAME;
}
else {
try {
destroyMethodName = bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
}
catch (NoSuchMethodException ex) {
try {
destroyMethodName = bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();
}
catch (NoSuchMethodException ex2) {
// no candidate destroy method found
}
}
}
}
}
beanDefinition.resolvedDestroyMethodName = (destroyMethodName != null ? destroyMethodName : "");
}
return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null);
}
方法解读:没啥好解读的了,直接重复贴一边注释就好了。尝试推断一个销毁方法。候选方法目前仅限于名为“close”或“shutdown”的公共无参数方法(无论是在本地声明还是继承)。如果没有找到这样的方法,则将给定BeanDefinition的“destroyMethodName”更新为null,否则将设置为推断方法的名称。该常量作为@Bean#destroyMethod属性的默认值,该常量的值也可以在XML中<bean destroy-method=“”>或属性中使用。还处理java.io.Closeable和AutoCloseable接口,并在实现bean时反射地调用“close”方法。
3.4 AbstractBeanFactory#hasDestructionAwareBeanPostProcessors:是否有感知销毁Bean后置处理器
方法调用链:由3.1中的requiresDestruction()调用过来
全路径:org.springframework.beans.factory.support.AbstractBeanFactory#hasDestructionAwareBeanPostProcessors
方法注释:返回该工厂是否持有一个DestructionAwareBeanPostProcessor,该DestructionAwareBeanPostProcessor将在关闭时应用于单例bean。
源码如下:
protected boolean hasDestructionAwareBeanPostProcessors() {
return !getBeanPostProcessorCache().destructionAware.isEmpty();
}
3.5 DisposableBeanAdapter.hasApplicableProcessors:是否有应用于当前Bean的销毁感知Bean后置处理器
方法调用链:由3.1中的requiresDestruction()调用过来
全路径:org.springframework.beans.factory.support.DisposableBeanAdapter#hasApplicableProcessors
方法注释:检查给定bean是否有应用于它的销毁感知后处理器。
源码如下:
public static boolean hasApplicableProcessors(Object bean, List<DestructionAwareBeanPostProcessor> postProcessors) {
if (!CollectionUtils.isEmpty(postProcessors)) {
for (DestructionAwareBeanPostProcessor processor : postProcessors) {
if (processor.requiresDestruction(bean)) {
return true;
}
}
}
return false;
}
经典的BeanPostProcessor处理了,不说了
3.6 DefaultSingletonBeanRegistry#registerDisposableBean:注册需要销毁的bean
方法调用链:从入口:registerDisposableBeanIfNecessary()调用进来
全路径:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#registerDisposableBean
方法注释:将给定的bean添加到此注册中心的销毁bean列表中。
源码如下:
private final Map<String, Object> disposableBeans = new LinkedHashMap<>();
public void registerDisposableBean(String beanName, DisposableBean bean) {
synchronized (this.disposableBeans) {
this.disposableBeans.put(beanName, bean);
}
}
方法解读:所谓的注册,其实就是将当前bean及一些信息,添加到一个缓存map中。等到需要用到的时候,直接遍历map就好
3.7 注册销毁Bean过程总结
整体来说分为2个步骤:
- 是单例bean,判断是否需要销毁。判断步骤如下:(不同的Spring版本细节不一样,但是整体是一致的)
- 当前Bean是否实现了
DisposableBean
接口,是则直接返回true;否则进行【推断销毁方法】流程 - 推断销毁方法
- BeanDefinition中是否指定了destroyMethod,且
destroyMethod==(inferred)
。如果是,则寻找当前bean下是否有close
方法或者shutdown
方法,是则直接返回销毁方法名称 - 或者当前Bean是否实现了AutoCloseable接口,是则直接返回销毁方法名称
- BeanDefinition中是否指定了destroyMethod,且
- 如果【推断销毁方法】也没有结果,则调用【感知销毁Bean后置处理器】DestructionAwareBeanPostProcessor.requiresDestruction(bean)进行判断
- ApplicationListenerDetector中直接使得,如果当前bean是ApplicationListener子类需要销毁
- InitDestroyAnnotationBeanPostProcessor中使得拥有@PreDestroy注解了的方法就是需要销毁
- 当前Bean是否实现了
- 如果需要销毁,则适配成DisposableBeanAdapter对象,并存入disposableBeans中(一个LinkedHashMap)
四、注册销毁Bean逻辑流程图
学习总结
- 学习了Bean销毁过程
- 学习了注册销毁Bean的逻辑
- 学习了如何自定义Bean销毁逻辑