供求信息

龙行有风,向虚拟机注册钩子,实现Bean对象的初始化和销毁方法

发布日期:2022-08-07 23:46    点击次数:98
  目录 一、前言 二、目标 三、设计 四、实现 1. 工程结构 2. 定义初始化和销毁方法的接口 3. Bean属性定义新增初始化和销毁 4. 执行 Bean 对象的初始化方法 5. 定义销毁方法适配器(接口和配置) 6. 创建Bean时注册销毁方法对象 7. 虚拟机关闭钩子注册调用销毁方法 五、测试 1. 事先准备 2. 配置文件 3. 单元测试 六、总结 七、系列推荐 一、前言

有什么方式,能给代码留条活路?

有人说:人人都是产品经理,那你知道吗,人人也都可以是码农程序员!就像:

编程就是;定义属性、创建方法、调用展示 Java 和 PHP 就像男人和女人,前者在乎架构化模块后,后者在乎那个颜色我喜欢 用心写,但不要不做格式化 初次和产品对接的三个宝:砖头、铁锹、菜刀,分别保证有用、可用、好用 从一行代码到一吨代码,开发越来越难,壁垒也越来越高

其实学会写代码并不难,但学会写好代码却很难。从易阅读上来说你的代码要有准确的命名和清晰的注释、从易使用上来说你的代码要具备设计模式的包装让对外的服务调用更简单、从易扩展上来说你的代码要做好业务和功能的实现分层。在易阅读、易使用、易扩展以及更多编码规范的约束下,还需要在开发完成上线后的交付结果上满足;高可用、高性能、高并发,与此同时你还会接到现有项目中层出不穷来自产品经理新增的需求。

怎么办?知道你在码砖,不知道你在盖哪个猪圈!

就算码的砖是盖的猪圈,也得因为猪多扩面积、改水槽、加饲料呀,所以根本没法保证你写完的代码就不会加需求。那么新加的需求如果是以破坏了你原有的封装了非常完美500行的 ifelse 咋办,拆了重盖吗?

兄嘚,给代码留条活路吧!你的代码用上了定义接口吗、接口继承接口吗、接口由抽象类实现吗、类继承的类实现了接口方法吗,而这些操作都是为了让你的程序逻辑做到分层、分区、分块,把核心逻辑层和业务封装层做好隔离,当有业务变化时候,只需要做在业务层完成装配,而底层的核心逻辑服务并不需要频繁变化,它们所增加的接口也更原子化,不具备业务语意。所以这样的实现方式才能给你的代码留条活路。如果还不是太理解,可以多看看《重学Java设计模式》和现在编写的《手撸Spring》,这里面都有大量的设计模式应用实践

二、目标

当我们的类创建的 Bean 对象,交给 Spring 容器管理以后,这个类对象就可以被赋予更多的使用能力。就像我们在上一章节已经给类对象添加了修改注册Bean定义未实例化前的属性信息修改和实例化过程中的前置和后置处理,这些额外能力的实现,都可以让我们对现有工程中的类对象做相应的扩展处理。

那么除此之外我们还希望可以在 Bean 初始化过程,执行一些操作。比如帮我们做一些数据的加载执行,链接注册中心暴漏RPC接口以及在Web程序关闭时执行链接断开,内存销毁等操作。如果说没有Spring我们也可以通过构造函数、静态方法以及手动调用的方式实现,但这样的处理方式终究不如把诸如此类的操作都交给 Spring 容器来管理更加合适。 因此你会看到到 spring.xml 中有如下操作:

需要满足用户可以在 xml 中配置初始化和销毁的方法,也可以通过实现类的方式处理,比如我们在使用 Spring 时用到的 InitializingBean, DisposableBean 两个接口。-其实还可以有一种是注解的方式处理初始化操作,不过目前还没有实现到注解的逻辑,后续再完善此类功能。

三、设计

可能面对像 Spring 这样庞大的框架,对外暴露的接口定义使用或者xml配置,完成的一系列扩展性操作,都让 Spring 框架看上去很神秘。其实对于这样在 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 方法关闭容器。 四、实现

1. 工程结构

small-spring-step-07 └── src     ├── main     │   └── java     │       └── cn.bugstack.springframework     │           ├── beans     │           │   ├── factory     │           │   │   ├── factory     │           │   │   │   ├── AutowireCapableBeanFactory.java     │           │   │   │   ├── BeanDefinition.java     │           │   │   │   ├── BeanFactoryPostProcessor.java     │           │   │   │   ├── BeanPostProcessor.java     │           │   │   │   ├── BeanReference.java     │           │   │   │   ├── ConfigurableBeanFactory.java     │           │   │   │   └── SingletonBeanRegistry.java     │           │   │   ├── support     │           │   │   │   ├── AbstractAutowireCapableBeanFactory.java     │           │   │   │   ├── AbstractBeanDefinitionReader.java     │           │   │   │   ├── AbstractBeanFactory.java     │           │   │   │   ├── BeanDefinitionReader.java     │           │   │   │   ├── BeanDefinitionRegistry.java     │           │   │   │   ├── CglibSubclassingInstantiationStrategy.java     │           │   │   │   ├── DefaultListableBeanFactory.java     │           │   │   │   ├── DefaultSingletonBeanRegistry.java     │           │   │   │   ├── DisposableBeanAdapter.java     │           │   │   │   ├── InstantiationStrategy.java     │           │   │   │   └── SimpleInstantiationStrategy.java       │           │   │   ├── support     │           │   │   │   └── XmlBeanDefinitionReader.java     │           │   │   ├── BeanFactory.java     │           │   │   ├── ConfigurableListableBeanFactory.java     │           │   │   ├── DisposableBean.java     │           │   │   ├── HierarchicalBeanFactory.java     │           │   │   ├── InitializingBean.java     │           │   │   └── ListableBeanFactory.java     │           │   ├── BeansException.java     │           │   ├── PropertyValue.java     │           │   └── PropertyValues.java      │           ├── context     │           │   ├── support     │           │   │   ├── AbstractApplicationContext.java      │           │   │   ├── AbstractRefreshableApplicationContext.java      │           │   │   ├── AbstractXmlApplicationContext.java      │           │   │   └── ClassPathXmlApplicationContext.java      │           │   ├── ApplicationContext.java      │           │   └── ConfigurableApplicationContext.java     │           ├── core.io     │           │   ├── ClassPathResource.java      │           │   ├── DefaultResourceLoader.java      │           │   ├── FileSystemResource.java      │           │   ├── Resource.java      │           │   ├── ResourceLoader.java      │           │   └── UrlResource.java     │           └── utils     │               └── ClassUtils.java     └── test         └── java             └── cn.bugstack.springframework.test                 ├── bean                 │   ├── UserDao.java                 │   └── UserService.java                 └── ApiTest.java 

工程源码:公众号「bugstack虫洞栈」,回复:Spring 专栏,获取完整源码

Spring 应用上下文和对Bean对象扩展机制的类关系,如图 8-4

图 8-4

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

2. 定义初始化和销毁方法的接口

cn.bugstack.springframework.beans.factory.InitializingBean

public interface InitializingBean {      /**      * Bean 处理了属性填充后调用      *       * @throws Exception      */     void afterPropertiesSet() throws Exception;  } 

cn.bugstack.springframework.beans.factory.DisposableBean

public interface DisposableBean {      void destroy() throws Exception;  } 
InitializingBean、DisposableBean,两个接口方法还是比较常用的,在一些需要结合 Spring 实现的组件中,经常会使用这两个方法来做一些参数的初始化和销毁操作。比如接口暴漏、数据库数据读取、配置文件加载等等。 3. Bean属性定义新增初始化和销毁

cn.bugstack.springframework.beans.factory.config.BeanDefinition

public class BeanDefinition {      private Class beanClass;      private PropertyValues propertyValues;      private String initMethodName;          private String destroyMethodName;          // ...get/set } 
在 BeanDefinition 新增加了两个属性:initMethodName、destroyMethodName,这两个属性是为了在 spring.xml 配置的 Bean 对象中,可以配置 init-method="initDataMethod" destroy-method="destroyDataMethod" 操作,最终实现接口的效果是一样的。只不过一个是接口方法的直接调用,另外是一个在配置文件中读取到方法反射调用 4. 执行 Bean 对象的初始化方法

cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {      private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();      @Override     protected Object createBean(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);         } catch (Exception e) {             throw new BeansException("Instantiation of bean failed", e);         }          // ...          addSingleton(beanName, bean);         return bean;     }      private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {         // 1. 执行 BeanPostProcessor Before 处理         Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);          // 执行 Bean 对象的初始化方法         try {             invokeInitMethods(beanName, wrappedBean, beanDefinition);         } catch (Exception e) {             throw new BeansException("Invocation of init method of bean[" + beanName + "] failed", e);         }          // 2. 执行 BeanPostProcessor After 处理         wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName);         return wrappedBean;     }      private void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Exception {         // 1. 实现接口 InitializingBean         if (bean instanceof InitializingBean) {             ((InitializingBean) bean).afterPropertiesSet();         }          // 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);         }     }  } 
抽象类 AbstractAutowireCapableBeanFactory 中的 createBean 是用来创建 Bean 对象的方法,在这个方法中我们之前已经扩展了 BeanFactoryPostProcessor、BeanPostProcessor 操作,这里我们继续完善执行 Bean 对象的初始化方法的处理动作。 在方法 invokeInitMethods 中,主要分为两块来执行实现了 InitializingBean 接口的操作,处理 afterPropertiesSet 方法。另外一个是判断配置信息 init-method 是否存在,执行反射调用 initMethod.invoke(bean)。这两种方式都可以在 Bean 对象初始化过程中进行处理加载 Bean 对象中的初始化操作,让使用者可以额外新增加自己想要的动作。 5. 定义销毁方法适配器(接口和配置)

cn.bugstack.springframework.beans.factory.support.DisposableBeanAdapter

public class DisposableBeanAdapter implements DisposableBean {      private final Object bean;     private final String beanName;     private String destroyMethodName;      public DisposableBeanAdapter(Object bean, String beanName, BeanDefinition beanDefinition) {         this.bean = bean;         this.beanName = beanName;         this.destroyMethodName = beanDefinition.getDestroyMethodName();     }      @Override     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);         }              }  } 
可能你会想这里怎么有一个适配器的类呢,因为销毁方法有两种甚至多种方式,目前有实现接口 DisposableBean、配置信息 destroy-method,两种方式。而这两种方式的销毁动作是由 AbstractApplicationContext 在注册虚拟机钩子后看,虚拟机关闭前执行的操作动作。 那么在销毁执行时不太希望还得关注都销毁那些类型的方法,它的使用上更希望是有一个统一的接口进行销毁,所以这里就新增了适配类,做统一处理。 6. 创建Bean时注册销毁方法对象

cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {      private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();      @Override     protected Object createBean(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);         } catch (Exception e) {             throw new BeansException("Instantiation of bean failed", e);         }          // 注册实现了 DisposableBean 接口的 Bean 对象         registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);          addSingleton(beanName, bean);         return bean;     }      protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {         if (bean instanceof DisposableBean 


上一篇:JVM是如何和“垃圾”发生关系的
下一篇:通过Hyper