手写简单的spring框架——my-mini-spring -- IOC篇 -- 复杂的IOC容器ApplicationContext(二)

项目的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销毁的逻辑)

Spring中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接口优先):

  1. 实现**InitializingBean接口,并实现其afterPropertiesSet()**方法
  2. bean定义(xml配置文件)中指定初始化方法,属性为:init-method
  3. (此源码中无法看出,源码中说的是外部的初始化方法)应该是在方法上加注解PostConstruct和PreDestroy等等

这里只实现前两种,第三种后面再实现(因为尚未知道原理 我猜测是注解驱动的自定义init方法)

1.2 实现初始化bean的前两种方式
  • BeanDefinition中需要增加属性表示初始化方法的名字

现BeanDefinition定义

image-20221112171524407

首先是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的调用时机是什么,由谁来负责调用,由又谁来负责执行实际逻辑呢?

调用时机

  1. 关闭或者刷新IOC容器时,需要删除所有的bean
  2. Bean实例被覆盖或者过期

关于DestroyBean的实际逻辑

IOC容器是可拓展的容器(实现了ConfigurableBeanFactory接口) --> 委托DisposableAdapter来处理实际的逻辑(即会判断是否有 自定义的destroy-method 或 使用注解添加的destroy (BeanProcessor))

实际上Spring中的destroyBean有两套逻辑

  1. AbstractAutoCapableBeanFactory中的 destroyBean(Object existingBean)(不用这个方法,这个是AutoCapableBeanFactory提供的接口 --> 几乎不用这个方法)

    传入一个Bean对象,然后包装成DisposableBeanAdapter再处理destroy方法

    @Override
    public void destroyBean(Object existingBean) {
        // 委托给DisposableBeanAdapter进行处理
       new DisposableBeanAdapter(
             existingBean, getBeanPostProcessorCache().destructionAware, getAccessControlContext()).destroy();
    }
    
  2. 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-methoddestroy-method等属性的判断

image-20221112173852578

这里的代码实现很简单,只需要在XmlBeanDefinitionReader中新加以上两个属性,并且增加对这种属性的解析即可

4、当前bean的生命周期

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>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值