神龙翱翔,注册虚拟机钩子,

2024-06-23 17:57:52 作者:6kYzQ!yIEmp_M6UkZ
实现Bean对象的初始化和销毁方法:\n目录:\n一、前言\n二、目标\n三、设计\n四、实现\n 1. 工程结构\n 2. 定义初始化和销毁方法的接口\n 3. Bean属性定义新增初始化和销毁\n 4. 执行Bean对象的初始化方法\n 5. 定义销毁方法适配器(接口和配置)\n 6. 创建Bean时注册销毁方法对象\n 7. 虚拟机关闭钩子注册调用销毁方法\n五、测试\n 1. 事先准备\n 2. 配置文件\n 3. 单元测试\n六、总结\n七、系列推荐\n一、前言\n有什么方式可以实现Bean对象的初始化和销毁方法?可以让代码有一些弹性吗?【407483】有人说:每个人都可以成为产品经理,但你知道吗,每个人也都可以成为码农程序员呢!

[[407483]]

有人认为:每个人都可以成为产品经理,但你知道吗,每个人也都可以成为编程程序员!比如说:

编程就是;定义属性、创建方法、调用展示 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 的属性中,在配置文件加载的过程中。在初始化操作时,工程可以利用反射方式调用Bean定义属性中配置的方法信息。如果采用接口实现的方式,就可以直接通过Bean对象调用相应接口定义的方法,((InitializingBean) bean).afterPropertiesSet(),两种方法实现的效果是相同的。在对Bean对象进行初始化阶段时,除了执行初始操作外,还会将destroy-method和DisposableBean接口的定义注册到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     │           │   ├── 工厂     │           │   │   ├── 工厂     │           │   │   │   ├── AutowireCapableBeanFactory.java       │           │   │   │   ├── BeanDefinition.java         │       {y.Java工程中有多个文件,包括DisposableBeanAdapter.java、InstantiationStrategy.java、SimpleInstantiationStrategy.java等。这些文件在不同的子目录中,如support、context等。除此之外,还有很多其他的java类,包括ClassPathResource.java、DefaultResourceLoader.java等。这些源代码可以在公众号"bugstack虫洞栈"中找到。 回复:Spring 专栏,获取完整源码

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

图 8-4

以上整个类图结构描述出来的就是本次新增 Bean 实例化过程中的初始化方法和销毁方法。我们实现了两种初始化和销毁方法,一种是通过xml配置,另一种是通过接口定义。因此,这里使用了InitializingBean和DisposableBean接口,同时也需要使用XmlBeanDefinitionReader将spring.xml配置信息加载到BeanDefinition中。{N} 另外,ConfigurableBeanFactory 接口定义了 destroySingletons 销毁方法,并且由其父类 DefaultSingletonBeanRegistry 在 AbstractBeanFactory 中实现了该接口所定义的 destroySingletons 方法。 此外,ConfigurableBeanFactory 接口还有一个 destroySingletons 方法的定义,该方法是由 AbstractBeanFactory 类继承的父类 DefaultSingletonBeanRegistry 实现的。这种设计方式可能对于一些程序员来说是不常见的,通常我们会选择直接由实现接口的类来完成接口的实现,而不是将接口的实现交给继承的父类处理。所以这个设计还是相当有趣的,是一种很好的隔离分层服务的设计方式 最后是关于在虚拟机注册钩子,确保在虚拟机关闭之前执行销毁操作。Runtime.调用getRuntime().addShutdownHook(new Thread(() -> System.out.println("关闭!");``)));

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

cn.bugstack.springframework.beans.factory.InitializingBean

public interface InitializingBean {      /**      * 在属性填充后调用      * @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 类 BeanDefinition {      private Class beanClass;      private 属性tyValues 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 {\n Object bean = null;\n try {\n bean = createBeanInstance(beanDefinition, beanName, 填充属性 args); // 用于给 Bean 填充属性的方法 applyPropertyValues(beanName, bean,beanDefinition); \n// 执行Bean的初始化方法和BeanPostProcessor的前置和后置处理方法\nbean = initializeBean(beanName, bean, beanDefinition);beanDefinition);}}}}}}catch(Exception e){ hrow new BeansException("bean实例化失败",addSingleton方法用于向容器中添加bean对象;\n使用initializeBean方法对bean对象进行初始化。BeanDefinition beanDefinition)       // 1. 在进行BeanPostProcessor处理之前执行 Object wrappedBean= applyBeanPostProcessorsBeforeInitialization(bean,beanName);   // 调用 Bean 对象的初始化方法   try {  invokeInitMethods(beanName, wrappedBean,beanDefinition); \ncatch (Exception e) {\n throw new BeansException("调用bean[" + beanName + "]的初始化方法失败", e);\ 2. 执行 BeanPostProcessor 的 After 处理,wrappedBean = applyBeanPostProcessorsAfterInitialization(bean,。。。调用initMethods(beanName, wrappedBean);\n私有void执行initMethods(String beanName, Object bean, ...){} BeanDefinition beanDefinition) throws Exception {         // 1. 实现接口 InitializingBean         如果 (bean 是 InitializingBean 接口的实例) {             ((InitializingBean) bean).afterPropertiesSet();         }          // 2. 配置信息 init-method {为了避免重复执行销毁}         String initMethodName = beanDefinition.getInitMethodName();         如果 (initMethodName 不为空) {             Method initMethod = beanDefinition.getBeanClass().getMethod(initMethodName);         如果 (initMethod 为null) {                 throw new BeansException("Could not find an init method named '" + initMethodName + "'on bean with name '" + beanName + "'");             }  {在这个步骤中,我们已经对 BeanFactoryPostProcessor 和 BeanPostProcessor 进行了扩展操作,现在我们将继续改进对 Bean 对象初始化方法的处理。在 invokeInitMethods 方法中,主要分为两部分来执行实现了 InitializingBean 接口的操作,处理 afterPropertiesSet 方法。在 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(O对象 bean, String beanName,重新改写和改述以下文本: BeanDefinition beanDefinition){\n this.bean = bean;\n this.beanName = beanName;\n this.destroyMethodName = beanDefinition.getDestroyMethodName();\ \n@Override\npublic void destroy() throws Exception{\n // 1. 实现接口DisposableBean\n if (bean instanceof DisposableBean){\n ((DisposableBean) bean).destroy();\n }\n // 2. 配置信息destroy-method(判断是为了避免二次执行销毁)\n if (StrUtil.isNotEmpty(destroyMethodName) && !(如果bean instanceof DisposableBean && "destroy".equals(this.destroyMethodName)){\n Method destroyMethod = bean.getClass().getMethod(destroyMethodName);\n if (null == destroyMethod){\n throw new BeansException("在bean " + beanName + " 中找不到名为'" + destroyMethodName + "' 的销毁方法");\n }\n destroyMethod.invoke(bean);\ \n你可能会对为什么这里有一个适配器类感到好奇。目前销毁对象的方式多种多样,可以通过实现 DisposableBean 接口或者在配置信息中设置 destroy-method 方式来进行销毁。这两种方式的销毁动作是在注册虚拟机挂钩后执行,即在虚拟机关闭之前进行。这两种销毁方式都是在注册虚拟机钩子后,由 AbstractApplicationContext 执行的操作。因此,在销毁时,我们不希望关注具体销毁哪些类型的方法,而是希望通过一个统一的接口来进行销毁。因此,我们新增了适配类来进行统一处理。 6. 在创建Bean时注册销毁方法的对象

cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
public abstract class AbstractAutowireCapableBeanFactory 扩展了AbstractBeanFactory,并实现了AutowireCapableBeanFactory接口 {      private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();      @Override     protected Object createBean(String beanName,BeanDefinition beanDefinition, Object[] args) throws BeansException {\n    Object bean = null;\n    \n    try {\n        bean = null;\n        \n        try {\n            bean = createBeanInstance(beanDefinition, beanName, args);\n            applyPropertyValues(bean, beanDefinition);\n            initializeBean(bean, beanName, beanDefinition);\n        } catch (BeansException ex) {\n            throw ex;\n        }\n        \n    } catch (BeansException ex) {\n        throw ex;\n    }\n    \n    return bean;\
 args); // 用属性值填充 Bean  applyPropertyValues(beanName, bean, beanDefinition);             // 调用 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法             bean = initializeBean(beanName, bean,抛出异常(e) new BeansException("bean实例化失败",beanDefinition);注册过程中,如果需要的话可以将Bean对象实现DisposableBean接口;通过registerDisposableBeanIfNecessary(beanName, bean, beanDefinition)方法来实现此功能;最后,通过addSingleton(beanName, ...)方法将其添加至单例对象中。为了确保DisposeBean如果必要的话进行注册,我们需要对bean进行注册。 如果bean实例是可以DisposeBean的类型,则我们会将bean注册为DisposeBean。 最后我们会返回bean。

            
          
在线咨询 拨打电话

电话

02088888888

微信二维码

微信二维码