手写Spring:第8章-初始化和销毁方法

news2024/10/2 20:33:01

文章目录

  • 一、目标:初始化和销毁方法
  • 二、设计:初始化和销毁方法
  • 三、实现:初始化和销毁方法
    • 3.1 工程结构
    • 3.2 Spring应用上下文和Bean对象扩展初始化和销毁类图
    • 3.3 定义初始化和销毁方法的接口
      • 3.3.1 定义初始化接口
      • 3.3.2 定义销毁接口
      • 3.3.3 定义销毁方法适配器(接口和配置)
    • 3.4 Bean属性定义新增初始化和销毁
      • 3.4.1 Bean对象
      • 3.4.2 XML处理Bean注册
      • 3.4.3 配置Bean工厂接口
      • 3.4.4 通用的注册表实现
      • 3.4.5 实现默认Bean创建的抽象Bean工厂超类
    • 3.5 虚拟机关闭钩子时注册调用销毁方法
      • 3.5.1 配置应用上下文接口
      • 3.5.2 应用上下文抽象类
  • 四、测试:初始化和销毁方法
    • 4.1 配置测试环境
      • 4.1.1 修改UserDao用户对象
      • 4.1.2 修改UserService用户对象
      • 4.1.3 修改配置文件
    • 4.2 单元测试
  • 五、总结:初始化和销毁方法

一、目标:初始化和销毁方法

💡 如何在 Spring 初始化和销毁对象时对这些内容做一些操作呢?

  • 当我们的类创建的 Bean 对象,交给 Spring 容器管理以后,这个类对象就可以被赋予更多的使用。
    • 如:给类对象添加了修改注册 Bean 定义未实例化前的属性信息修改和初始化过程中的前置和后置处理。
  • 除此之外,我们还希望在 Bean 初始化过程,执行一些操作。
    • 如:数据的加载执行,链接注册中心暴露 RPC 接口,在 Web 程序关闭时执行链接断开、内存销毁等。

在这里插入图片描述

  • 需要满足用户可以在 xml 中配置初始化和销毁的方法,也可以通过实现类的方式处理。
    • 如:使用 Spring 时用到的 InitializingBeanDisposableBean 两个接口。

二、设计:初始化和销毁方法

💡 技术设计:spring.xml配置文件配置初始化和销毁方法

  • 对于 这样在 Bean 容器初始化过程中额外添加的处理操作,就是预先执行了一个定义好的接口或者是反射调用类中 xml 中配置的方法。
  • 最终只要按照接口定义实现,就会有 Spring 容器在处理的过程中进行调用即可。

在这里插入图片描述

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

三、实现:初始化和销毁方法

3.1 工程结构

spring-step-07
|-src
	|-main
	|	|-java
	|		|-com.lino.springframework
	|			|-beans
	|			|	|-factory
	|			|	|	|-config
	|			|	|	|	|-AutowireCapableBeanFactory.java
	|			|	|	|	|-BeanDefinition.java
	|			|	|	|	|-BeanFactoryPostProcessor.java
	|			|	|	|	|-BeanPostProcessor.java
	|			|	|	|	|-BeanReference.java
	|			|	|	|	|-ConfigurableBeanFactory.java
	|			|	|	|	|-SingletonBeanRegistry.java
	|			|	|	|-support
	|			|	|	|	|-AbstractAutowireCapableBeanFactory.java
	|			|	|	|	|-AbstractBeabDefinitionReader.java
	|			|	|	|	|-AbstractBeabFactory.java
	|			|	|	|	|-BeabDefinitionReader.java
	|			|	|	|	|-BeanDefinitionRegistry.java
	|			|	|	|	|-CglibSubclassingInstantiationStrategy.java
	|			|	|	|	|-DefaultListableBeanFactory.java
	|			|	|	|	|-DefaultSingletonBeanRegistry.java
	|			|	|	|	|-DisposableBeanAdapter.java
	|			|	|	|	|-InstantiationStrategy.java
	|			|	|	|	|-SimpleInstantiationStrategy.java
	|			|	|	|-support
	|			|	|	|	|-XMLBeanDefinitionReader.java
	|			|	|	|-BeanFactory.java
	|			|	|	|-ConfigurableListableBeanFactory.java
	|			|	|	|-DisposableBean.java
	|			|	|	|-HierarcgicalBeanFactory.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
	|			|-util
	|			|	|-ClassUtils.java
	|-test
		|-java
			|-com.lino.springframework.test
                |-bean
                |	|-UserDao.java
                |	|-UserService.java
                |-ApiTest.java
		|-resources
			|-spring.xml

3.2 Spring应用上下文和Bean对象扩展初始化和销毁类图

在这里插入图片描述

  • 整个类图结构描述出来的是本次新增 Bean 实例化过程中的初始化方法和销毁方法。
  • 一共实现了两种方式的初始化和销毁方法,xml 配置和定义接口。
    • 定义接口InitializingBeanDisposableBean
    • xml 配置XmlBeanDefinitionReader 加载 spring.xml 配置信息到 BeanDefinition 中。
  • 接口 ConfigurableBeanFactory 定义了 destroySingletons 方法。
    • 把实现接口的操作又交给继承的父类处理,是一种隔离分层服务的设计方式
  • 最后就是向虚拟机注册钩子,保证在虚拟机关闭之前,执行销毁操作。
    • Runtime.getRuntime().addShutdownHook(new Thread(this::close))

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

  • InitializingBeanDisposableBean,两个接口方法都是比较常用的,在结合一些 Spring 实现的组件中,经常会使用这两个方法来做一些参数的初始化和销毁操作。
    • 如:接口暴露、数据库数据读取、配置文件加载等。

3.3.1 定义初始化接口

InitializingBean.java

package com.lino.springframework.beans.factory;

/**
 * @description: 实现此接口的 Bean 对象,会在 BeanFactory 设置属性后做出相应的处理
 * 如:执行自定义初始化,或者仅仅检查是否设置了所有强制属性
 */
public interface InitializingBean {

    /**
     * Bean 处理属性填充后调用
     *
     * @throws Exception 异常
     */
    void afterPropertiesSet() throws Exception;
}

3.3.2 定义销毁接口

DisposableBean.java

package com.lino.springframework.beans.factory;

/**
 * @description: 销毁接口
 */
public interface DisposableBean {

    /**
     * 销毁方法
     *
     * @throws Exception 异常
     */
    void destroy() throws Exception;
}

3.3.3 定义销毁方法适配器(接口和配置)

DisposableBeanAdapter.java

package com.lino.springframework.beans.factory.support;

import cn.hutool.core.util.StrUtil;
import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.DisposableBean;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import java.lang.reflect.Method;

/**
 * @description: 销毁方法适配器
 */
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 && "destory".equals(this.destroyMethodName))) {
            Method destroyMethod = bean.getClass().getMethod(destroyMethodName);
            if (null == destroyMethod) {
                throw new BeansException("Could't find a destroy method named '" + destroyMethodName + "' on bean with name '" + beanName + "'");
            }
            destroyMethod.invoke(bean);
        }
    }
}
  • 这是一个适配器的类,因为销毁方法有两种甚至多种方式。目前有 实现接口 DisposableBean配置信息 spring.xml 两种方式。
    • 而这两种方式的销毁动作是由 AbstractApplicationContext 在注册虚拟机钩子后,虚拟机关闭前执行的操作动作。
  • 那么在销毁执行时不太希望还得关注都销毁哪些类型的方法,它的使用更希望是有一个统一的接口进行销毁,所以这里添加了适配类,做统一处理。

3.4 Bean属性定义新增初始化和销毁

3.4.1 Bean对象

BeanDefinition.java

package com.lino.springframework.beans.factory.config;

import com.lino.springframework.beans.PropertyValues;

/**
 * @description: Bean 对象信息定义
 */
public class BeanDefinition {

    /**
     * bean对象
     */
    private Class beanClass;

    private PropertyValues propertyValues;

    private String initMethodName;

    private String destroyMethodName;

    public BeanDefinition(Class beanClass) {
        this.beanClass = beanClass;
        this.propertyValues = new PropertyValues();
    }

    public BeanDefinition(Class beanClass, PropertyValues propertyValues) {
        this.beanClass = beanClass;
        this.propertyValues = propertyValues != null ? propertyValues : new PropertyValues();
    }

    public Class getBeanClass() {
        return beanClass;
    }

    public void setBeanClass(Class beanClass) {
        this.beanClass = beanClass;
    }

    public PropertyValues getPropertyValues() {
        return propertyValues;
    }

    public void setPropertyValues(PropertyValues propertyValues) {
        this.propertyValues = propertyValues;
    }

    public String getInitMethodName() {
        return initMethodName;
    }

    public void setInitMethodName(String initMethodName) {
        this.initMethodName = initMethodName;
    }

    public String getDestroyMethodName() {
        return destroyMethodName;
    }

    public void setDestroyMethodName(String destroyMethodName) {
        this.destroyMethodName = destroyMethodName;
    }
}
  • BeanDefinition 新增加了两个属性:initMethodNamedestroyMethodName
    • 这两个属性是为了在 spring.xml 配置的 Bean 对象中,可以配置 init-method="initDataMethod" destroy-method="destroyDataMethod" 操作,最终实现接口的效果是一样的。
    • 一个是接口方法的直接调用,另外一个是在配置文件中读取到方法反射调用。

3.4.2 XML处理Bean注册

XMLBeanDefinitionReader.java

package com.lino.springframework.beans.factory.xml;

import cn.hutool.core.lang.copier.SrcToDestCopier;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.PropertyValue;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.beans.factory.config.BeanReference;
import com.lino.springframework.beans.factory.support.AbstractBeanDefinitionReader;
import com.lino.springframework.beans.factory.support.BeanDefinitionRegistry;
import com.lino.springframework.core.io.Resource;
import com.lino.springframework.core.io.ResourceLoader;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.io.IOException;
import java.io.InputStream;

/**
 * @description: XML处理Bean注册
 */
public class XMLBeanDefinitionReader extends AbstractBeanDefinitionReader {

    ...

    protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException {
        Document doc = XmlUtil.readXML(inputStream);
        Element root = doc.getDocumentElement();
        NodeList childNodes = root.getChildNodes();

        for (int i = 0; i < childNodes.getLength(); i++) {
            // 判断元素
            if (!(childNodes.item(i) instanceof Element)) {
                continue;
            }
            // 判断对象
            if (!"bean".equals(childNodes.item(i).getNodeName())) {
                continue;
            }

            // 解析标签
            Element bean = (Element) childNodes.item(i);
            String id = bean.getAttribute("id");
            String name = bean.getAttribute("name");
            String className = bean.getAttribute("class");
            String initMethod = bean.getAttribute("init-method");
            String destroyMethodName = bean.getAttribute("destroy-method");

            // 获取 Class, 方便获取类中的名称
            Class<?> clazz = Class.forName(className);
            // 优先级 id > name
            String beanName = StrUtil.isNotEmpty(id) ? id : name;
            if (StrUtil.isEmpty(beanName)) {
                beanName = StrUtil.lowerFirst(clazz.getSimpleName());
            }

            // 定义bean
            BeanDefinition beanDefinition = new BeanDefinition(clazz);
            beanDefinition.setInitMethodName(initMethod);
            beanDefinition.setDestroyMethodName(destroyMethodName);

            // 读取属性并填充
            for (int j = 0; j < bean.getChildNodes().getLength(); j++) {
                // 判断元素
                if (!(bean.getChildNodes().item(j) instanceof Element)) {
                    continue;
                }
                // 判断对象
                if (!"property".equals(bean.getChildNodes().item(j).getNodeName())) {
                    continue;
                }
                // 解析标签:property
                Element property = (Element) bean.getChildNodes().item(j);
                String attrName = property.getAttribute("name");
                String attrValue = property.getAttribute("value");
                String attrRef = property.getAttribute("ref");
                // 获取属性值:引入对象、值对象
                Object value = StrUtil.isNotEmpty(attrRef) ? new BeanReference(attrRef) : attrValue;
                // 创建属性信息
                PropertyValue propertyValue = new PropertyValue(attrName, value);
                beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
            }
            if (getRegistry().containsBeanDefinition(beanName)) {
                throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");
            }
            // 注册 BeanDefinition
            getRegistry().registerBeanDefinition(beanName, beanDefinition);
        }
    }
}
  • Bean 属性定义增加初始化和销毁后,还需要在 XMLBeanDefinitionReader 类中,增加对新增属性的读取,并添加到 BeanDefinition 中。

3.4.3 配置Bean工厂接口

ConfigurableBeanFactory.java

package com.lino.springframework.beans.factory.config;

import com.lino.springframework.beans.factory.HierarchicalBeanFactory;

/**
 * @description: 配置Bean工厂接口
 */
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {

    String SCOPE_SINGLETON = "singleton";

    String SCOPE_PROTOTYPE = "prototype";

    /**
     * 添加修改新实例化 Bean 对象的扩展点
     *
     * @param beanPostProcessor 新实例化 Bean 对象
     */
    void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);

    /**
     * 销毁单例
     */
    void destroySingletons();
}
  • 新增加 destroySingletons() 销毁单例方法。

3.4.4 通用的注册表实现

DefaultSingletonBeanRegistry.java

package com.lino.springframework.beans.factory.support;

import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.DisposableBean;
import com.lino.springframework.beans.factory.config.SingletonBeanRegistry;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @description: 通用的注册表实现
 */
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

    private Map<String, Object> singletonObjects = new HashMap<>();

    private final Map<String, DisposableBean> disposableBeans = new HashMap<>();

    @Override
    public Object getSingleton(String beanName) {
        return singletonObjects.get(beanName);
    }

    @Override
    public void registerSingletonBean(String beanName, Object singletonObject) {
        singletonObjects.put(beanName, singletonObject);
    }

    public void registerDisposableBean(String beanName, DisposableBean bean) {
        disposableBeans.put(beanName, bean);
    }

    public void destroySingletons() {
        Set<String> 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();
            } catch (Exception e) {
                throw new BeansException("Destroy method on bean with name '" + beanName + "' threw an exception", e);
            }
        }
    }
}
  • 新增加 Map<String, DisposableBean> disposableBeans 销毁方法 Map 属性,并添加根据对象名称获取销毁对象的方法。
    • registerDisposableBean(String beanName, DisposableBean bean)

3.4.5 实现默认Bean创建的抽象Bean工厂超类

AbstractAutowireCapableBeanFactory.java

package com.lino.springframework.beans.factory.support;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.PropertyValue;
import com.lino.springframework.beans.PropertyValues;
import com.lino.springframework.beans.factory.DisposableBean;
import com.lino.springframework.beans.factory.InitializingBean;
import com.lino.springframework.beans.factory.config.AutowireCapableBeanFactory;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.beans.factory.config.BeanPostProcessor;
import com.lino.springframework.beans.factory.config.BeanReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * @description: 实现默认bean创建的抽象bean工厂超类
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {

    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) {
        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);

        registerSingletonBean(beanName, bean);
        return bean;
    }

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

    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(wrappedBean, 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) && !(bean instanceof InitializingBean)) {
            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);
        }
    }

    ...
}

执行 Bean 对象的初始化方法

  • 抽象类 AbstractAutowireCapableBeanFactory 中的 createBean 是用来创建 Bean 对象的方法,在这个方法中我们已经扩展了 BeanFactoryPostProcessorBeanPostProcessor 操作,现在继续完善执行 Bean 对象的初始化方法的处理动作。
  • 在方法 invokeInitMethods 中,主要分两块来执行。
    • 实现了 InitializingBean 接口的操作,处理 afterPropertiesSet 方法。
    • 另外一个是判断配置信息 init-method 是否存在,执行反射调用 initMethod.invoke(bean)
    • 这两种方式都可以在 Bean 对象初始化过程中进行处理加载 Bean 对象中的初始化操作,让使用者可以额外新增加自己想要的动作。

创建 Bean 时注册销毁方法对象

  • 在创建 Bean 对象的实例的时候,需要把销毁方法保存起来,方便后续执行销毁动作进行调用。
  • 那么这个销毁方法的具体方法信息,会被注册到 DefaultSingletonBeanRegistry 中新增加的 Map<String, DisposableBean> disposableBeans 属性中去。
    • 因为这个接口的方法最终可以被类 AbstractApplicationContextclose 方法通过 getBeanFactory().destroySingletons() 调用。
  • 在注册销毁方法的时候,会根据是接口类型和配置类型统一交给 DisposableBeanAdapter 销毁适配器类来做统一处理。
    • 实现某个接口的类可以被 instanceof 判断或者强转后调用接口方法

3.5 虚拟机关闭钩子时注册调用销毁方法

3.5.1 配置应用上下文接口

ConfigurableApplicationContext.java

package com.lino.springframework.context;

import com.lino.springframework.beans.BeansException;

/**
 * @description: SPI 接口配置应用上下文
 */
public interface ConfigurableApplicationContext extends ApplicationContext {

    /**
     * 刷新容器
     *
     * @throws BeansException 对象异常
     */
    void refresh() throws BeansException;

    /**
     * 注册虚拟机钩子方法
     */
    void registerShutdownHook();

    /**
     * 手动关闭
     */
    void close();
}
  • 首先需要在 ConfigurableApplicationContext 接口中定义注册虚拟机钩子的方法 registerShutdownHook 和手动执行关闭的方法 close

3.5.2 应用上下文抽象类

AbstractApplicationContext.java

package com.lino.springframework.context.support;

import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.ConfigurableListableBeanFactory;
import com.lino.springframework.beans.factory.config.BeanFactoryPostProcessor;
import com.lino.springframework.beans.factory.config.BeanPostProcessor;
import com.lino.springframework.context.ConfigurableApplicationContext;
import com.lino.springframework.core.io.DefaultResourceLoader;
import java.util.Map;

/**
 * @description: 抽象应用上下文
 */
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {

    @Override
    public void refresh() throws BeansException {
        // 1.创建 BeanFactory,并加载 BeanDefinition
        refreshBeanFactory();

        // 2.获取 BeanFactory
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();

        // 3.在 Bean 实例化之前,执行 BeanFactoryPostProcess
        invokeBeanFactoryPostProcessor(beanFactory);

        // 4.BeanPostProcessor 需要提前与其他 Bean 对象实例化之前执行注册操作
        registerBeanPostProcessor(beanFactory);

        // 5.提前实例化单例 Bean 对象
        beanFactory.preInstantiateSingletons();
    }

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

    @Override
    public void close() {
        getBeanFactory().destroySingletons();
    }

    ...
}
  • 这里主要体现了关于注册钩子和关闭的方法实现,根据方法 Runtime.getRuntime().addShutdownHook(new Thread(this::close))

四、测试:初始化和销毁方法

4.1 配置测试环境

4.1.1 修改UserDao用户对象

UserDao.java

package com.lino.springframework.test.bean;

import java.util.HashMap;
import java.util.Map;

/**
 * @description: 模拟用户DAO类
 */
public class UserDao {

    private static Map<String, String> hashMap = new HashMap<>();

    public void initDataMethod() {
        System.out.println("执行:init-method");
        hashMap.put("10001", "张三");
        hashMap.put("10002", "李四");
        hashMap.put("10003", "王五");
    }

    public void destroyDataMethod() {
        System.out.println("执行:destroy-method");
        hashMap.clear();
    }

    public String queryUserName(String uId) {
        return hashMap.get(uId);
    }
}
  • 添加初始化方法 initDataMethod 和销毁方法 destroyDataMethod

4.1.2 修改UserService用户对象

UserService.java

package com.lino.springframework.test.bean;

import com.lino.springframework.beans.factory.DisposableBean;
import com.lino.springframework.beans.factory.InitializingBean;

/**
 * @description: 模拟用户 Bean 对象
 */
public class UserService implements InitializingBean, DisposableBean {

    private String uId;
    private String company;
    private String location;
    private UserDao userDao;

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

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

    /**
     * 查询用户信息
     */
    public String queryUserInfo() {
        return userDao.queryUserName(uId) + "," + company + "," + location;
    }

    public String getuId() {
        return uId;
    }

    public void setuId(String uId) {
        this.uId = uId;
    }

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location;
    }

    public UserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}
  • 实现接口 InitializingBeanDisposableBean 的两个方法 afterPropertiesSet()destroy(),处理相应的初始化和销毁方法的动作。

4.1.3 修改配置文件

spring.xml

<?xml version="1.0" encoding="utf-8" ?>
<beans>
    <bean id="userDao" class="com.lino.springframework.test.bean.UserDao"
          init-method="initDataMethod"
          destroy-method="destroyDataMethod"
    />

    <bean id="userService" class="com.lino.springframework.test.bean.UserService">
        <property name="uId" value="10001"/>
        <property name="company" value="阿里"/>
        <property name="location" value="杭州"/>
        <property name="userDao" ref="userDao"/>
    </bean>
</beans>
  • 配置文件新增加了 init-method="initDataMethod"destroy-method="destroyDataMethod" 两个配置。
  • 这两个配置是为了加入到 BeanDefinition 定义类之后写入到类 DefaultListableBeanFactory 中的 beanDefinitionMap 属性中去。

4.2 单元测试

ApiTest.java

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

    // 3.获取Bean对象调用方法
    UserService userService = applicationContext.getBean("userService", UserService.class);
    String result = userService.queryUserInfo();
    System.out.println("测试结果:" + result);
}
  • 测试方法中新增加了一个注册钩子的动作。applicationContext.registerShutdownHook()

测试结果

执行:init-method
执行:UserService.afterPropertiesSet
测试结果:张三,阿里,杭州
执行:UserService.destroy
执行:destroy-method
  • 从测试结果来看,xi能增加的初始化和销毁方法都有输出结果了。

五、总结:初始化和销毁方法

  • 主要完成了关于初始化和销毁方法的两种方法。
    • 使用接口定义:implements InitializingBean, DisposableBean
    • spring.xml 中配置 init-method="initDataMethod" destroy-method="destroyDataMethod"
  • 具体在 AbstractAutowireCapableBeanFactory 完成初始化方法和 AbstractApplicationContext 完成销毁动作的具体实现过程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/983160.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【 OpenGauss源码学习 —— 列存储(analyze)(三)】

列存储&#xff08;analyze&#xff09; acquire_sample_rows 函数RelationGetNumberOfBlocks 函数BlockSampler_Init 函数anl_init_selection_state 函数BlockSampler_GetBlock 函数ReadBufferExtendedPageGetMaxOffsetNumber 函数HeapTupleSatisfiesVacuum 函数heapCopyTuple…

微软8月系统更新引发问题:虚拟内存分页文件出现错误

微软的八月系统更新引发了一系列问题&#xff0c;其中包括“UNSUPPORTED_PROCESSOR”蓝屏错误和文件管理器故障。尽管微软已经修复了前者&#xff0c;但据国外科技媒体Windows Latest报道&#xff0c;仍有用户反馈在非微星设备上出现“fault in nonpaged area”蓝屏错误。 如果…

信息技术 安全技术 信息安全管理测量

声明 本文是学习信息技术 安全技术 信息安全管理 测量. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 范围 为了评估按照GB/T 22080-2008规定实施的信息安全管理体系&#xff08;Information Security Management System&#xff0c;简称ISMS&#…

STM32移植FAT文件系统

所谓“移植”&#xff0c;就是打通FAT源码和物理设备之间的软件接口。 FAT源码早就被公益组织给写好了&#xff0c;直接下载源码。但是FAT作为顶层应用程序&#xff0c;它需要面对的底层物理设备是不确定的&#xff0c;那么底层的物理设备驱动程序就需要程序员来自己写。物理设…

VMware虚拟机挂起后再关机出现问题,连接不上外网解决方法,ifconfig无ens33

原文地址 Centos7 重启后ens33消失&#xff08;外部连接不上&#xff09;Centos7 重启后ens33消失&#xff08;外部连接不上&#xff09;_ens33不见了_瘦身小蚂蚁的博客-CSDN博客 解决方法&#xff1a;执行以下4个命令。 systemctl stop NetworkManager # 关闭NetworkManage…

《DevOps实践指南》- 读书笔记(二)

DevOps实践指南 Part 2 从何处开始5. 选择合适的价值流作为切入点5.1 绿地项目与棕地项目5.2 兼顾记录型系统和交互型系统5.3 从最乐于创新的团队开始5.4 扩大 DevOps 的范围5.5 小结 6. 理解、可视化和运用价值流6.1 确定创造客户价值所需的团队6.2 针对团队工作绘制价值流图6…

优思学院|精益生产与柔性制造:现代制造业的双重理念

现代制造业正不断演进&#xff0c;出现了许多新的生产理念和方法。在这其中&#xff0c;精益生产和柔性制造是两个引人注目且重要的理念。这两者不仅对企业的生产流程产生了深远的影响&#xff0c;还在提高效率、适应市场需求方面发挥着关键作用。 理念背景和核心原则 精益生…

pcd格式转ot/bt

1.具体实现可以看高博的bloghttps://www.cnblogs.com/gaoxiang12/p/5041142.html 2.问题&#xff1a;在编译octomap_tutor 源码时会报错。 修改方法&#xff1a;把cmakelists.txt里的c11改成c14

Android 文字转语音播放实现

1&#xff0c;TextToSpeech类是android自带的&#xff0c;但是部分设备需要支持TTS需要增加语音库&#xff0c;我使用的是讯飞语音&#xff08;离线的哦&#xff09;。请自行下载并安装讯飞语音APK&#xff0c;然后到系统设置中设置TTS功能默认使用该选项。有自带TTS库的可以省…

数字图像处理-图像压缩

数字图像处理-图像压缩 一、图像压缩1.1 图像压缩的意义1.2 图像的冗余信息1.2.1 编码冗余1.2.2 空间冗余1.2.3 时间冗余 二、一些基本的压缩方法2.1 霍夫曼编码2.2 行程编码2.3 算术编码2.4 LZW编码 三、数字图像水印3.1 简单的可见水印3.2LSB不可见水印 一、图像压缩 1.1 图…

亚马逊美国站干粉灭火器UL安全测试标准要求UL299测试报告

UL299干粉灭火器是一种常见的灭火设备&#xff0c;外观一般为红色罐体&#xff0c;装有干粉灭火剂。它主要以高速喷射干粉灭火剂来抑制火灾并起到灭火作用。该灭火器采用压力容器和喷射装置&#xff0c;具有紧凑结构、方便携带等特点。对于想要在亚马逊美国站上架干粉灭火器的卖…

SpingBoot整合Sa-Token框架(1)

一、文档参考&#xff1a;框架介绍 (sa-token.cc) 框架生态——开源项目 (sa-token.cc) 二、与SpingBoot整合 1、创建项目 在 IDE 中新建一个 SpringBoot 项目&#xff0c;例如&#xff1a;sa-token-demo-springboot&#xff08;不会的同学请自行百度或者参考&#xff1a;Sp…

在EC2上对SELinux故障进行紧急恢复以及排查的思路及方法

概述 SELinux&#xff0c;全称Security-Enhanced Linux&#xff0c;是一个为系统提供强制访问控制机制的安全模块&#xff0c;安装并启用SELinux模块的操作系统会为每个进程和系统资源打上一个特殊的安全标记&#xff0c;称为SELinux上下文&#xff0c;并根据SELinux上下文信息…

高阶数据结构-----三种平衡树的实现以及原理(未完成)

TreeMap和TreeSet的底层实现原理就是红黑树 一)AVL树: 1)必须是一棵搜索树:前提是二叉树&#xff0c;任取一个节点&#xff0c;它的左孩子的Key小于父亲节点的Key小于右孩子节点的Key&#xff0c;中序遍历是有序的&#xff0c;按照Key的大小进行排列&#xff0c;高度平衡的二叉…

WINGREEN 034STM4-00-200-RS 输入输出模块

WINGREEN 034STM4-00-200-RS 输入输出模块是一种工业自动化设备&#xff0c;通常用于各种应用领域&#xff0c;以实现数字输入和输出功能。这些模块可以在不同行业的自动化系统中发挥关键作用&#xff0c;以下是一些可能的应用领域&#xff1a; 制造业&#xff1a;WINGREEN 034…

kubeadm搭建kubernetes(k8s)

kubeadm搭建kubernetes&#xff08;k8s&#xff09; 一、环境准备1.所有节点&#xff0c;关闭防火墙规则&#xff0c;关闭selinux&#xff0c;关闭swap交换2.修改主机名3.所有节点修改hosts文件4.调整内核参数5.生效参数 二、 安装软件1.所有节点安装docker2.所有节点安装kubea…

机器学习开源工具BatteryML,一站式分析与预测电池性能

编者按&#xff1a;天下苦锂电池寿命久矣&#xff0c;时闻“开车出&#xff0c;推车回”&#xff0c;又闻“充电两小时&#xff0c;待机两分钟”&#xff0c;亦闻“气温骤降&#xff0c;请注意电池保暖”……随着以锂离子电池为动力源的产品&#xff0c;如手机、电脑、新能源汽…

基于Java+SpringBoot+Vue前后端分离人事管理系统设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

软路由vs传统路由器ip:选择合适的网络设备

在构建和管理网络基础设施时&#xff0c;选择正确的网络设备至关重要。本文将介绍软路由IP和传统路由器两种常见的网络设备&#xff0c;并探讨它们各自特点、优势以及应用场景&#xff0c;帮助读者了解并选择最适合他们需求的网络设备。 什么是软路由IP&#xff1f; 软路由IP是…

vscode软件中不显示文件前的图标

问题&#xff1a; 解决方案&#xff1a; 效果&#xff1a;