项目的Github地址:github仓库
Gitee地址:gitee仓库
个人博客地址:sillybaka的博客
此篇所在的分支为:
init-and-destroy
该篇
1、实现了Bean生命周期中的自定义初始化
2、实现了Bean生命周期中的自定义销毁
复杂的IOC容器
3、bean的初始化以及销毁
在前文中提到了,bean在实例化、自动装配属性后还需要进行初始化,
1、那么用户要如何定义初始化方法,并且让Spring执行呢?
同时,在前文bean的生命周期中,缺少了bean的销毁这一过程,
2、简单地销毁bean方法会由spring提供,而用户需要提供自定义的销毁方法。
这里再放一次Spring中Bean标准的生命周期图
这里我们要实现的是 5,6(Bean初始化的逻辑) 和 9,10(Bean销毁的逻辑)
1、初始化
1.1 Spring中初始化bean的几种方式
Spring中初始化bean逻辑的源码
AbstractAutowireCapableBeanFactory#invokeInitMethods()
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
// 检查当前bean是否实现了InitializingBean接口
boolean isInitializingBean = (bean instanceof InitializingBean);
// 如果当前bean实现了InitializingBean接口,并且指定的init-method不为afterPropertiesSet
if (isInitializingBean && (mbd == null || !mbd.hasAnyExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
// 执行bean类实现的 InitializingBean接口的afterPropertiesSet()方法 (自定义初始化方法)
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
// 如果当前bean没有实现InitializingBean接口,但bean定义中指定了init-method,同时也没有外部的初始化方法与init-method的名字相同
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.hasAnyExternallyManagedInitMethod(initMethodName)) {
// 那么就执行bean定义中指定的初始化方法
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
通过看源码可以知道,spring初始化bean有以下三种方法(
不可同时执行,以实现InitializingBean接口优先
):
- 实现**
InitializingBean接口
,并实现其afterPropertiesSet()
**方法- 在bean定义(xml配置文件)中
指定初始化方法
,属性为:init-method
(此源码中无法看出,源码中说的是外部的初始化方法)
应该是在方法上加注解PostConstruct和PreDestroy等等这里只实现前两种,第三种后面再实现(因为尚未知道原理 我猜测是注解驱动的自定义init方法)
1.2 实现初始化bean的前两种方式
- BeanDefinition中需要增加属性表示初始化方法的名字
现BeanDefinition定义
首先是Spring提供的最原始的,用于自定义初始化方法的接口
InitializableBean接口
public interface InitializingBean {
/**
* 在beanFactory为bean装配完属性后调用,用于自定义初始化方法
*/
void afterPropertiesSet();
}
然后,我们要将自定义初始化init-method融入到Bean的生命周期中(所以要更新创建Bean的实际逻辑)
更新doCreateBean方法(创建Bean的实际逻辑,位于AbstractAutowireCapableBeanFactory中)
/**
* 创建Bean实例的实际逻辑
*/
public <T> T doCreateBean(String beanName,BeanDefinition<T> beanDefinition){
// 实例化
T beanInstance = INSTANTIATION_STRATEGY.instantiation(beanDefinition);
// 自动装配属性
autoWirePropertyValues(beanName,beanInstance,beanDefinition);
// 执行bean的初始化方法
try {
beanInstance = initializeBean(beanName,beanInstance,beanDefinition);
} catch (InvocationTargetException | IllegalAccessException e) {
throw new BeansException("bean initialize error",e);
}
return beanInstance;
}
/**
* 初始化bean的逻辑方法
*/
public <T> T initializeBean(String beanName, T bean, BeanDefinition<T> beanDefinition) throws InvocationTargetException, IllegalAccessException {
// 初始化之前执行后置处理器
T wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean,beanName);
// 执行自定义初始化方法
invokeInitMethods(wrappedBean,beanName,beanDefinition);
// 初始化之后执行后置处理器
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
return wrappedBean;
}
/**
* 对指定bean执行自定义的初始化方法
* @param existingBean 已实例化的bean
* @param beanName bean名字
*/
public <T> void invokeInitMethods(T existingBean, String beanName, BeanDefinition<T> beanDefinition) throws InvocationTargetException, IllegalAccessException {
boolean isInitializingBean = (existingBean instanceof InitializingBean &&
!"afterPropertiesSet".equals(beanDefinition.getInitMethodName()));
if(isInitializingBean){
((InitializingBean) existingBean).afterPropertiesSet();
}
String initMethodName = beanDefinition.getInitMethodName();
if(StrUtil.isNotBlank(initMethodName)){
Method initMethod = ClassUtil.getPublicMethod(existingBean.getClass(), initMethodName);
if(initMethod == null){
throw new BeansException("the bean named [" + beanName + "] specify initialization method ["+ initMethodName +"] does not exist");
}
initMethod.invoke(existingBean);
}
}
2、销毁
2.1 Spring中销毁bean的几种方式
DisposableBeanAdapter源码(此类用于处理判断是否有自定义destroy方法,并将其找到)
(这里Spring采用了适配器模式,DisposableBeanAdapter适配了DisposableBean接口
(实现destroy()方法),和Runnable接口
(使其可以作为异步线程任务执行destroy()方法
)
// DiposableBeanAdapter的类定义
public DisposableBeanAdapter(Object bean, String beanName, RootBeanDefinition beanDefinition,
List<DestructionAwareBeanPostProcessor> postProcessors, @Nullable AccessControlContext acc) {
Assert.notNull(bean, "Disposable bean must not be null");
this.bean = bean;
this.beanName = beanName;
this.nonPublicAccessAllowed = beanDefinition.isNonPublicAccessAllowed();
// 判断传入的bean是否实现了DisposableBean接口、bean定义中有无外部的destroy方法
this.invokeDisposableBean = (bean instanceof DisposableBean &&
!beanDefinition.hasAnyExternallyManagedDestroyMethod(DESTROY_METHOD_NAME));
// 试着使用bean对象(其实现的接口)和bean定义来推断destroy方法的名字
// --> 其实就是找xml的bean定义中有无指定destroy-method
String destroyMethodName = inferDestroyMethodIfNecessary(bean, beanDefinition);
// 如果有指定destroy-method 并且没有实现DisposableBean接口、也没有外部的destroy方法
if (destroyMethodName != null &&
!(this.invokeDisposableBean && DESTROY_METHOD_NAME.equals(destroyMethodName)) &&
!beanDefinition.hasAnyExternallyManagedDestroyMethod(destroyMethodName)) {
// 有无实现AutoCloseable接口,并且看看destroyMethodName是否与自动关闭的方法名字相同
this.invokeAutoCloseable = (bean instanceof AutoCloseable && CLOSE_METHOD_NAME.equals(destroyMethodName));
.............
.............
// 最后会将此指定的destroy-method 设置为 destroy()的实际逻辑
this.destroyMethod = destroyMethod;
}
}
// 加载beanPostProcessors
this.beanPostProcessors = filterPostProcessors(postProcessors, bean);
this.acc = acc;
}
// 执行destroy的实际逻辑
public void destroy() {
// 如果用于Destroy的Processors不为空(即外部的destroy方法),则执行
if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
processor.postProcessBeforeDestruction(this.bean, this.beanName);
}
}
// 如果实现了DisposableBean接口 并且bean定义指定的destroy方法不叫destroy
if (this.invokeDisposableBean) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking destroy() on bean with name '" + this.beanName + "'");
}
try {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
((DisposableBean) this.bean).destroy();
return null;
}, this.acc);
}
else {
// 执行已经实现的DisposableBean接口的destroy方法
((DisposableBean) this.bean).destroy();
}
}
catch (Throwable ex) {
String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
if (logger.isDebugEnabled()) {
logger.warn(msg, ex);
}
else {
logger.warn(msg + ": " + ex);
}
}
}
// 实现了AutoCloseable接口 并且自动关闭的方法名为destroy
if (this.invokeAutoCloseable) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking close() on bean with name '" + this.beanName + "'");
}
try {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
((AutoCloseable) this.bean).close();
return null;
}, this.acc);
}
else {
((AutoCloseable) this.bean).close();
}
}
catch (Throwable ex) {
String msg = "Invocation of close method failed on bean with name '" + this.beanName + "'";
if (logger.isDebugEnabled()) {
logger.warn(msg, ex);
}
else {
logger.warn(msg + ": " + ex);
}
}
}
// 否则执行在bean定义中指定的destroy-method
else if (this.destroyMethod != null) {
invokeCustomDestroyMethod(this.destroyMethod);
}
// 否则执行在bean定义中指定的destroy-method
else if (this.destroyMethodName != null) {
Method destroyMethod = determineDestroyMethod(this.destroyMethodName);
if (destroyMethod != null) {
invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(destroyMethod, this.bean.getClass()));
}
}
}
通过看源码可以知道,在Spring中销毁bean的方式有四种:
- 实现了**
DisposableBean接口
,并且实现了destroy()**方法- 在xml配置文件中的**
bean定义
,指定了destroy-method
**- 实现了**
AutoCloseable接口
,并且指定了自动关闭执行的方法,且该方法名为destroy
**()- 定义**
DestructionAwareBeanPostProcessor
**,用于在外部控制(拓展)bean的destroy在这里也只实现前面两种,第三种后续再实现
在Spring中,BeanFactory将destroy bean的逻辑全权交给了DisposableBeanAdapter
,这里我也会采用这样的实现
在这里需要先思考
一个问题
:destroyBean的调用时机是什么,由谁来负责调用,由又谁来负责执行实际逻辑呢?
调用时机
关闭或者刷新IOC容器时
,需要删除所有的bean- Bean实例
被覆盖或者过期
时关于DestroyBean的实际逻辑
IOC容器是
可拓展
的容器(实现了ConfigurableBeanFactory接口) --> 委托DisposableAdapter
来处理实际的逻辑(即会判断是否有 自定义的destroy-method 或 使用注解添加的destroy (BeanProcessor))
实际上Spring中的destroyBean有两套逻辑
AbstractAutoCapableBeanFactory
中的 destroyBean(Object existingBean)(不用这个方法,这个是AutoCapableBeanFactory提供的接口 --> 几乎不用这个方法)
传入一个Bean对象,然后
包装成DisposableBeanAdapter
再处理destroy方法@Override public void destroyBean(Object existingBean) { // 委托给DisposableBeanAdapter进行处理 new DisposableBeanAdapter( existingBean, getBeanPostProcessorCache().destructionAware, getAccessControlContext()).destroy(); }
DefaultSingletonRegistry
中会有一个特殊的注册表,保存了所有实现了DisposableBean接口或者bean定义中指定了destroy-method的单例bean包装而成的DisposableAdapter对象(在Bean注册时注入
,单例bean才会有,相当于缓存了对所有单例bean的自定义destroy方法的判断结果)
DefaultSingletonRegistry
中的destroySingleton方法
和destroySingletons方法
,是**destroyBean
**的模板逻辑(ConfigurableBeanFactory中定义的接口 --> Spring常用这个)
// 销毁指定的单例bean public void destroySingleton(String beanName) { // 删除缓存中的bean removeSingleton(beanName); DisposableBean disposableBean; // 从特殊的注册表中取出该bean对应的DisposableAdapter synchronized (this.disposableBeans) { disposableBean = (DisposableBean) this.disposableBeans.remove(beanName); } // 实际destroy逻辑 destroyBean(beanName, disposableBean); } // 销毁所有的单例bean public void destroySingletons() { if (logger.isTraceEnabled()) { logger.trace("Destroying singletons in " + this); } synchronized (this.singletonObjects) { this.singletonsCurrentlyInDestruction = true; } String[] disposableBeanNames; synchronized (this.disposableBeans) { disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet()); } // 倒序取出所有的DisposableAdapter for (int i = disposableBeanNames.length - 1; i >= 0; i--) { // 再一个个处理删除单个的逻辑 destroySingleton(disposableBeanNames[i]); } // 清除所有的缓存 this.containedBeanMap.clear(); this.dependentBeanMap.clear(); this.dependenciesForBeanMap.clear(); clearSingletonCache(); } // 销毁一个bean的实际逻辑 protected void destroyBean(String beanName, @Nullable DisposableBean bean) { Set<String> dependencies; synchronized (this.dependentBeanMap) { dependencies = this.dependentBeanMap.remove(beanName); } if (dependencies != null) { if (logger.isTraceEnabled()) { logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies); } // 销毁该bean所有依赖的bean for (String dependentBeanName : dependencies) { destroySingleton(dependentBeanName); } } if (bean != null) { try { // 真正执行当前bean的自定义destroy方法 bean.destroy(); } catch (Throwable ex) { if (logger.isWarnEnabled()) { logger.warn("Destruction of bean with name '" + beanName + "' threw an exception", ex); } } } ....................
2.2 实现destroy bean的前两种方式
首先是最原始的,DisposableBean接口,这是Spring提供的最原始的可以自定义destroy方法的接口
DisposableBean接口
/**
* beans可实现的接口,若bean实现了该接口,BeanFactory应在其销毁时调用{@link #destroy()}
* Date: 2022/10/21
* Time: 23:32
*
* @Author SillyBaka
**/
public interface DisposableBean {
/**
* 在bean销毁时,由其所属的BeanFactory调用,可自定义实现
*/
void destroy();
}
其次就是DisposableBeanAdapter,它实现了对Bean的自定义destroy方法的判断,并且缓存了destroy方法(new的时候自动判断,或者 自身作为新的线程任务 交给新的线程来异步判断 因为实现了Runnable接口)
DisposableBeanAdapter
/**
* 在给定的bean实例上执行各种销毁步骤的适配器
* 实现了{@link sillybaka.springframework.beans.factory.DisposableBean},{@link Runnable}接口
*
*
* @see sillybaka.springframework.beans.factory.DisposableBean
* @see Runnable
* @Author SillyBaka
**/
public class DisposableBeanAdapter implements DisposableBean,Runnable {
private static final String DESTROY_METHOD_NAME = "destroy";
private static final String CLOSEABLE_METHOD_NAME = "close";
private String beanName;
private Object bean;
private BeanDefinition beanDefinition;
private String destroyMethodName;
// 是否实现了DisposableBean接口
private boolean invokeDisposableBean;
// 是否实现了AutoCloseable接口 并且关闭方法名为destroy
private boolean isAutoCloseable;
private Method destroyMethod;
public DisposableBeanAdapter(String beanName, Object bean, BeanDefinition beanDefinition){
this.beanName = beanName;
this.bean = bean;
this.beanDefinition = beanDefinition;
// 实现了DisposableBean接口 并且没有外部管理的destroy方法和destroy同名 --> 避免执行同一方法两次
invokeDisposableBean = (bean instanceof DisposableBean) && !DESTROY_METHOD_NAME.equals(beanDefinition.getDestroyMethodName());
// 查看bean定义中是否指定了destroy-method
String destroyMethodName = beanDefinition.getDestroyMethodName();
if(StrUtil.isNotBlank(destroyMethodName)){
this.destroyMethodName = destroyMethodName;
}
}
/**
* 作为一个DisposableBean接口执行
*/
@Override
public void destroy() {
if(invokeDisposableBean){
((DisposableBean) bean).destroy();
}
if(StrUtil.isNotBlank(destroyMethodName)){
Method destroyMethod = ClassUtil.getPublicMethod(bean.getClass(), destroyMethodName);
invokeCustomDestroyMethod(destroyMethod);
}else if(this.destroyMethod != null){
invokeCustomDestroyMethod(this.destroyMethod);
}
}
/**
* 作为一个异步线程任务执行
*/
@Override
public void run() {
destroy();
}
/**
* 执行指定的destroyMethod,封装了处理异常的逻辑
* @param destroyMethod 自定义的destroy方法
*/
public void invokeCustomDestroyMethod(Method destroyMethod){
int parameterCount = destroyMethod.getParameterCount();
if(parameterCount > 0){
throw new BeansException("The specified destroy method [" + destroyMethod.getName() + "] has more than zero parameter");
}
Object[] args = new Object[0];
try {
destroyMethod.invoke(bean,args);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
然后,我们还要将destroy-method融入到Bean的生命周期中,所以也要更新创建Bean的实际逻辑
更新doCreateBean方法(创建Bean的实际逻辑,位于AbstractAutowireCapableBeanFactory中)
/**
* 创建Bean实例的实际逻辑
*/
public <T> T doCreateBean(String beanName,BeanDefinition<T> beanDefinition){
// 实例化
T beanInstance = INSTANTIATION_STRATEGY.instantiation(beanDefinition);
// 自动装配属性
autoWirePropertyValues(beanName,beanInstance,beanDefinition);
// 执行bean的初始化方法
try {
beanInstance = initializeBean(beanName,beanInstance,beanDefinition);
} catch (InvocationTargetException | IllegalAccessException e) {
throw new BeansException("bean initialize error",e);
}
// 检查当前bean是否有自定义的destroy方法,若有则需要注册进注册表
registerDisposableBeanIfNecessary(beanName,beanInstance,beanDefinition);
return beanInstance;
}
/**
* 判断bean是否实现了DisposableBean接口 或者指定了destroy()方法,若是则注册进注册表中
*/
public <T> void registerDisposableBeanIfNecessary(String beanName, T bean, BeanDefinition<T> beanDefinition){
// 当bean实现了DisposableBean接口 或者指定了destroy()方法时 new一个适配器注册到容器中
if(bean instanceof DisposableBean || StrUtil.isNotBlank(beanDefinition.getDestroyMethodName())){
// 将Bean包装为一个适配器,注册到容器中
registerDisposableBean(beanName,new DisposableBeanAdapter(beanName,bean,beanDefinition));
}
}
3、修改XmlBeanDefinitionReader的逻辑
Spring中的xml格式
<bean class="xxx" init-method="initMethod", destroy-method="destroyMethod"></bean>
所以当前的XmlBeanDefinitionReader需要增加对init-method
、destroy-method
等属性的判断
这里的代码实现很简单,只需要在XmlBeanDefinitionReader中新加以上两个属性,并且增加对这种属性的解析即可
4、当前bean的生命周期
测试
测试类代码
public class BeanInitAndDestroyTest {
@Test
public void testInitMethod(){
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:testInit.xml");
}
@Test
public void testDestroyMethod(){
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:testDestroy.xml");
classPathXmlApplicationContext.close();
}
}
测试用XML文件
testInit.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="init1" class="sillybaka.springframework.entity.TestInit" init-method="init">
<property name="name" value="测试不重名的init"/>
<property name="age" value="666"/>
</bean>
<bean id="init2" class="sillybaka.springframework.entity.TestInit" init-method="afterPropertiesSet">
<property name="name" value="测试重名的init"/>
<property name="age" value="666"/>
</bean>
</beans>
testDestroy.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="testDestroy1" class="sillybaka.springframework.entity.TestDestroy" destroy-method="destroy">
<property name="name" value="我是测试重名的destroy"/>
<property name="age" value="666"/>
</bean>
<bean id="testDestroy2" class="sillybaka.springframework.entity.TestDestroy" destroy-method="myDestroy">
<property name="name" value="我是测试不重名的destroy"/>
<property name="age" value="999"/>
</bean>
</beans>