[Spring手撸专栏学习笔记]——实现Bean对象的初始化和销毁方法

[Spring手撸专栏学习笔记]——实现Bean对象的初始化和销毁方法,第1张

[Spring手撸专栏学习笔记]——实现Bean对象的初始化和销毁方法

本文是学习《Spring 手撸专栏》第 8 章笔记,主要记录我的一些debug调试过程,方便后期复习。具体学习,大家可以去看一下这个专栏,强烈推荐。

目标

当我们的类创建的 Bean 对象,交给 Spring 容器管理以后,这个类对象就可以被赋予更多的使用能力。
那么除此之外我们还希望可以在 Bean 初始化过程,执行一些 *** 作。如果说没有Spring我们也可以通过构造函数、静态方法以及手动调用的方式实现,但这样的处理方式终究不如把诸如此类的 *** 作都交给 Spring 容器来管理更加合适。 因此你会看到到 spring.xml 中有如下 *** 作:

需要满足用户可以在 xml 中配置初始化和销毁的方法,也可以通过实现类的方式处理 设计

其实对于这样在 Bean 容器初始化过程中额外添加的处理 *** 作,无非就是预先执行了一个定义好的接口方法或者是反射调用类中xml中配置的方法,最终你只要按照接口定义实现,就会有 Spring 容器在处理的过程中进行调用而已。整体设计结构如下图:

在 spring.xml 配置中添加 init-method、destroy-method 两个注解,在配置文件加载的过程中,把注解配置一并定义到 BeanDefinition 的属性当中。这样在 initializeBean 初始化 *** 作的工程中,就可以通过反射的方式来调用配置在 Bean 定义属性当中的方法信息了。另外如果是接口实现的方式,那么直接可以通过 Bean 对象调用对应接口定义的方法即可,((InitializingBean) bean).afterPropertiesSet(),两种方式达到的效果是一样的。除了在初始化做的 *** 作外,destroy-method 和 DisposableBean 接口的定义,都会在 Bean 对象初始化完成阶段,执行注册销毁方法的信息到 DefaultSingletonBeanRegistry 类中的 disposableBeans 属性里,这是为了后续统一进行 *** 作。这里还有一段适配器的使用,因为反射调用和接口直接调用,是两种方式。所以需要使用适配器进行包装,下文代码讲解中参考 DisposableBeanAdapter 的具体实现 -关于销毁方法需要在虚拟机执行关闭之前进行 *** 作,所以这里需要用到一个注册钩子的 *** 作,如:Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println(“close!”))); 这段代码你可以执行测试,另外你可以使用手动调用 ApplicationContext.close 方法关闭容器。

以上整个类图结构描述出来的就是本次新增 Bean 实例化过程中的初始化方法和销毁方法。因为我们一共实现了两种方式的初始化和销毁方法,xml配置和定义接口,所以这里既有 InitializingBean、DisposableBean 也有需要 XmlBeanDefinitionReader 加载 spring.xml 配置信息到 BeanDefinition 中。另外接口 ConfigurableBeanFactory 定义了 destroySingletons 销毁方法,并由 AbstractBeanFactory 继承的父类 DefaultSingletonBeanRegistry 实现 ConfigurableBeanFactory 接口定义的 destroySingletons 方法。这种方式的设计可能数程序员是没有用过的,都是用的谁实现接口谁完成实现类,而不是把实现接口的 *** 作又交给继承的父类处理。所以这块还是蛮有意思的,是一种不错的隔离分层服务的设计方式最后就是关于向虚拟机注册钩子,保证在虚拟机关闭之前,执行销毁 *** 作。Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println(“close!”)));

debug调试

主线

(1)主线分支1(7)主线分支2

      public void test_xml() {
        // 1.初始化 BeanFactory  (1)
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
        applicationContext.registerShutdownHook();//(7)

        // 2. 获取Bean对象调用方法
        UserService userService = applicationContext.getBean("userService", UserService.class);
        String result = userService.queryUserInfo();
        System.out.println("测试结果:" + result);
    }

先所注册bean *** 作
这里前面已经提到很多了,这里就不再重复调试了,我说一下不一样的地方

(1)AbstractAutowireCapableBeanFactory 主线分支1
给Bean填充属性时,会把我们xml文件中的配置填充进去(我们后文的UserDao的destroyDataMethod属性就是在这里填充的)
在这里执行Bean 的初始化方法,注意他是在填充了属性之后再调用的。
注册实现了DisposableBean 接口的Bean对象

 //利用反射进行bean的实例化
    @Override
    protected Object creatBean(String beanName,BeanDefinition beanDefinition,Object[] args) throws BeansException {
       Object bean = null;
       try {
           bean = createBeanInstance(beanDefinition,beanName,args);
           //给Bean填充属性
           applyPropertyValues(beanName,bean,beanDefinition);
           //执行Bean 的初始化方法和BeanPostProcessor的前置和后置处理方法(这些是我们自己额外定义的)
           bean = initializeBean(beanName,bean,beanDefinition);//(2)
       } catch (Exception e) {
           throw new BeansException("Instantiation of bean failed",e);
       }

       //注册实现了DisposableBean 接口的Bean对象
        registerDisposableBeanIfNecessary(beanName,bean,beanDefinition);//(5)

       addSingleton(beanName,bean);
        return bean;
    }

(2) AbstractAutowireCapableBeanFactory

private Object initializeBean(String beanName,Object bean,BeanDefinition beanDefinition){

        //1 执行BeanPostProcessor Before处理
        Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);

   
        try {
            invokeInitMethods(beanName, wrappedBean, beanDefinition);//(3)
        }catch (Exception e){
            throw new BeansException("Invocation of init method of bean["+beanName+"] failed",e);
        }


        //2 执行BeanPostProcessor After处理
        wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
        return wrappedBean;
    }

(3) AbstractAutowireCapableBeanFactory
就是在这里调用我们自己实现了InitializingBean的类中的afterPropertiesSet方法的。

private void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Exception {
        //这两种方式都可以在 Bean 对象初始化过程中进行处理加载 Bean 对象中的初始化 *** 作,让使用者可以额外新增加自己想要的动作。
        //1 实现了接口InitializingBean
        if (bean instanceof InitializingBean){
            ((InitializingBean)bean).afterPropertiesSet();//(4)
        }

        //2 配置了信息init-method
        String initMethodName = beanDefinition.getInitMethodName();
        if (StrUtil.isNotEmpty(initMethodName)){
            Method initMethod = beanDefinition.getBeanClass().getMethod(initMethodName);
            if (null == initMethod){
                throw new BeansException("Could not find an init method named '"+initMethodName+"'on bean with name '"+beanName+"'");
            }
            //通过反射来调用方法
            initMethod.invoke(bean);
        }
    }

(4)UserService implements InitializingBean, DisposableBean

 @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("执行:UserService.afterPropertiesSet");
    }

(5)AbstractAutowireCapableBeanFactory
把继承了DisposableBean接口的,或者在xml配置了destroyMethodName的bean注册进去

  protected void registerDisposableBeanIfNecessary(String beanName,Object bean,BeanDefinition beanDefinition){
        if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())){
            //(6)
            registerDisposableBean(beanName,new DisposableBeanAdapter(bean,beanName,beanDefinition));
        }
    }

(6)DefaultSingletonBeanRegistry
这里其实就是把需要执行销毁方法的bean放入链表中,后面注销是从这里拿出来即可

private final Map disposableBeans = new HashMap<>();
public void registerDisposableBean(String beanName,DisposableBean bean){
        disposableBeans.put(beanName,bean);
    }

(7) AbstractApplicationContext 主线分支2

public void registerShutdownHook() {
        Runtime.getRuntime().addShutdownHook(new Thread(this::close));
    }
 @Override
    public void close() {
        getBeanFactory().destroySingletons(); //(8)
    }

(8)DefaultSingletonBeanRegistry
把之前注册进disposableBeans的disposableBean取出,然后disposableBean.destroy();

 private final Map disposableBeans = new HashMap<>();
 public void destroySingletons(){
        Set keySet = this.disposableBeans.keySet();
        Object[] disposableBeanNames = keySet.toArray();
        for (int i = disposableBeanNames.length-1;i>=0;i--){
            Object beanName = disposableBeanNames[i];
            DisposableBean disposableBean = disposableBeans.remove(beanName);
            try {
                disposableBean.destroy(); //(9)
            }catch (Exception e){
                throw new BeansException("Destroy method on bean with name '"+beanName+"'threw an exception",e);
            }
        }

    }

(9) DisposableBeanAdapter
无论是实现了接口的,还是通过xml配置的,统一在这个适配器中进行销毁处理,这样就不用单独再实现两个类进行处理了

public void destroy() throws Exception {
        //1 实现了接口DisposableBean
        if(bean instanceof DisposableBean){
            ((DisposableBean)bean).destroy();
        }

        //2 配置了信息destroy-method{潘多是为了避免二次执行销毁}
        if (StrUtil.isNotEmpty(destroyMethodName) && !(bean instanceof DisposableBean && "destroy".equals(this.destroyMethodName))){
            Method destroyMethod = bean.getClass().getMethod(destroyMethodName);
            if (null == destroyMethod){
                throw new BeansException("Couldn't find a destroy method named '"+destroyMethodName+"'on bean with name'"+beanName+"'");
            }
            destroyMethod.invoke(bean);
        }
    }

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5720523.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-18
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存