【Spring源码系列】Bean生命周期-Bean销毁
创始人
2024-03-16 22:17:52

前言

Spring给我们提供了一种当bean销毁时调用某个方法的方式。那么,Spring底层到底是如何实现的呢?接下来,我们将从源码+案例的方式来解析:spring如何实现当bean销毁时调用某个方法的。

一、Bean销毁介绍

bean销毁的时机

spring容器关闭的时候(调用close())方法的时候,那么对于实现destroy方法的bean,就会开始执行各自自定义的销毁逻辑。

提示:
是spring容器关闭的时候调用bean销毁逻辑,不是垃圾回收、程序意外终止、程序正常终止…的时候。

spring注册DestroyBean时机

1、注册DisposableBeans。在‘初始化后’会对BeanDefinition进行判断,判断该BeanDefinition是否具备destroy方法,如果具备则把BeanDefinition注册到DisposableBeans。具体如何判断的,我们下面会讲;
在这里插入图片描述
2、执行destroy方法。当调用close方法的时候,会遍历DisposableBeans执行每一个销毁方法

常用的定义bean销毁方式

此处不仅仅写了代码示例,也把源码贴出来进行验证。

使用@PreDestroy注解

代码示例:

@Component
public class UserService {@Autowiredprivate OrderService orderService;public void test(){System.out.println(orderService);}@PreDestroypublic void myDestroyUserServiceMethod () {System.out.println("UserService#myDestroyUserServiceMethod");}
}

源码:

	protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {// hasDestroyMethod: 实现了DisposableBean或者AutoCloseable接口	,或者创建bean的时候手动指定了销毁方法( 比如@Bean(destroyMethod = "destory")、xml中的bean标签中指定destroyMethod)return (bean.getClass() != NullBean.class && (DisposableBeanAdapter.hasDestroyMethod(bean, mbd) ||// @PreDestroy注解(hasDestructionAwareBeanPostProcessors() && DisposableBeanAdapter.hasApplicableProcessors(bean, getBeanPostProcessorCache().destructionAware))));}

代码调试:
CommonAnnotationBeanPostProcessor:
在这里插入图片描述UserService#myDestroyUserServiceMethod销毁方法在Spring容器启动的时候就已经被记录在CommonAnnotationBeanPostProcessor中了,当调用org.springframework.beans.factory.support.AbstractBeanFactory#requiresDestruction判断该bean是否定义销毁逻辑的时候返回的是true:
在这里插入图片描述

实现DisposableBean或者AutoCloseable接口

代码示例:

@Component
public class UserService implements DisposableBean {@Autowiredprivate OrderService orderService;public void test(){System.out.println(orderService);}@Overridepublic void destroy () {System.out.println("UserService#destroy");}
}

源码:

	public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {// 是否实现了这两个接口中的一个if (bean instanceof DisposableBean || bean instanceof AutoCloseable) {return true;}// 判断BeanDefinition是否指定了销毁方法return inferDestroyMethodIfNecessary(bean, beanDefinition) != null;}

源码调试:
UserService 实现了 DisposableBean 接口,所以DisposableBeanAdapter.hasDestroyMethod(bean, mbd)返回true,且可以发现CommonAnnotationBeanPostProcessor#lifecycleMetadataCache集合中的UserService.class并没指定destroyMethods:
在这里插入图片描述

手动指定destroy方法(@Bean、XML)

手动指定dstroy方法有两种方式:
1、@Bean注解方式指定destroyMethod;
2、XML文件中< bean >标签里面指定destry-method;

public class OrderService {public void destroy () {System.out.println("OrderService#destroy");}
}
@ComponentScan("com.cms")
public class AppConfig {@Bean(destroyMethod = "destroy")public OrderService createOrderService () {return new OrderService();}}

在这里插入图片描述

手动指定destroy方法((inferred))

代码示例:

public class OrderService {// 必须是close方法public void close () {System.out.println("OrderService#destroy");}
}
@ComponentScan("com.cms")
public class AppConfig {@Bean(destroyMethod = "(inferred)")public OrderService createOrderService () {return new OrderService();}}

源码:

	@Nullableprivate static String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {// 判断BeanDefinition是否指定了销毁方法(比如创建bean的时候(@Bean、xml),手动指定destroyMethod)String destroyMethodName = beanDefinition.resolvedDestroyMethodName;// 下面这种定义销毁的方式,不常用。流程:先定义销毁方法-(inferred) ,然后调用close方法。if (destroyMethodName == null) {destroyMethodName = beanDefinition.getDestroyMethodName(); //if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||(destroyMethodName == null && bean instanceof AutoCloseable)) {// Only perform destroy method inference or Closeable detection// in case of the bean not explicitly implementing DisposableBeandestroyMethodName = null;if (!(bean instanceof DisposableBean)) {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);}

手动指定destroy方法(MergedBeanDefinitionPostProcessor后置处理器设置销毁方法)

@Component
public class MyMergeBdfPostProcesser implements MergedBeanDefinitionPostProcessor {@Overridepublic void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) {if (beanName.equals("myDisposableBean3")){beanDefinition.setDestroyMethodName("a");}}
}@Component
public class MyDisposableBean3 {public void a()  {System.out.println("MyMergeBdfPostProcesser-后置处理器销毁");}
}

或者

@Component
public class MyMergeBdfPostProcesser2 implements MergedBeanDefinitionPostProcessor {@Overridepublic void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) {if (beanName.equals("myDisposableBean4")){beanDefinition.setDestroyMethodName("(inferred)");}}
}@Component
public class MyDisposableBean4 {//	public void close()  {
//		System.out.println("close销毁");
//	}public void shutdown()  {System.out.println("shutdown销毁");}
}

二、Bean销毁-源码分析

声明关键点

1、原型bean即使定义了销毁方法,但是执行不会调用销毁方法。

源代码

源码位置:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

// 只做一件事情:注册实现了'销毁'方法的bean。
registerDisposableBeanIfNecessary(beanName, bean, mbd);
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);// if(不是'多例'bean && 有销毁方法)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));}}}

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...