手写Spring7(实现bean的初始化、销毁)

news2024/11/13 23:16:54

文章目录

  • 目标
  • 设计流程
  • 项目结构
  • 一、实现
    • 1、 定义初始化和销毁方法的接口
    • 2、Bean属性定义新增初始化和销毁
    • 3、执行 Bean 对象的初始化方法
    • 4、定义销毁方法适配器(接口和配置)
    • 5、定义注册销毁对象、销毁实现
    • 6、创建Bean时注册销毁方法对象
    • 7、虚拟机关闭钩子注册调用销毁方法
  • 二、测试
    • 1、事先准备
    • 2、配置文件
    • 3、单元测试


目标

在Spring中,我们希望可以在 Bean 初始化过程,执行一些操作。比如帮我们做一些数据的加载执行,链接注册中心暴露RPC接口以及在Web程序关闭时执行链接断开,内存销毁等操作

在这里插入图片描述
1、实现xml定义初始化、销毁的名称,最终通过反射执行方法名的函数,来达到在初始化和销毁执行自定义的逻辑处理

在这里插入图片描述
2、实现 通过实现接口的形式,来完成和上述xml同样的功能


设计流程

在这里插入图片描述

1、在 spring.xml 配置中添加 init-method、destroy-method 两个注解,在配置文件加载的过程中,把注解配置一并定义到 BeanDefinition 的属性当中。这样在 initializeBean 初始化操作的工程中,就可以通过反射的方式来调用配置在 Bean 定义属性当中的方法信息了。

2、如果是接口实现的方式,那么直接可以通过 Bean 对象调用对应接口定义的方法即可,((InitializingBean) bean).afterPropertiesSet(),两种方式达到的效果是一样的。


项目结构

在这里插入图片描述

下面这里有个有意思的点
这里AbstractAutowireCapableBeanFactory抽象类,实现ConfigurableBeanFactory接口,继承自DefaultSingletonBeanRegistry
ConfigurableBeanFactory接口定义了destroySingletons,DefaultSingletonBeanRegistry实现了他,AbstractAutowireCapableBeanFactory调用destroySingletons的关系。
可以简单理解为(A继承B实现C时,C的接口方法由A继承的父类B实现)

上面这种也是可以复用到通常的业务系统开发中进行处理一些复杂逻辑的功能分层,做到程序的可扩展、易维护等特性。

在这里插入图片描述


一、实现

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

/**
 * 初始化bean接口
 */
public interface InitializingBean {

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

    void destroy() throws Exception;
}

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


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

package cn.ljc.springframework.beans.factory.config;

import cn.ljc.springframework.beans.PropertyValue;
import cn.ljc.springframework.beans.PropertyValues;

/**
 * @desc Bean定义
 * @Author: ljc
 * @Date: 2022/11/28 10:35
 */
public class BeanDefinition {

    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();
    }


	// .. get/set
}

在 BeanDefinition 新增加了两个属性:initMethodName、destroyMethodName,这两个属性是为了在 spring.xml 配置的 Bean 对象中,可以配置 init-method=“initDataMethod” destroy-method=“destroyDataMethod” 操作,通过在xml配置文件中读取到方法反射调用

/**
 * 解析XML处理Bean注册
 */
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
    }

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
        super(registry, resourceLoader);
    }


    @Override
    public void loadBeanDefinitions(Resource resource) throws BeansException {
        try {
            InputStream inputStream = resource.getInputStream();
            doLoadBeanDefinitions(inputStream);
        } catch (IOException | ClassNotFoundException e) {
            throw new BeansException("IOException parsing XML document from " + resource, e);
        }
    }

    @Override
    public void loadBeanDefinitions(Resource... resources) throws BeansException {
        for (Resource resource : resources) {
            loadBeanDefinitions(resource);
        }
    }

    @Override
    public void loadBeanDefinitions(String location) throws BeansException {
        ResourceLoader resourceLoader = getResourceLoader();
        Resource resource = resourceLoader.getResource(location);
        loadBeanDefinitions(resource);
    }

    @Override
    public void loadBeanDefinitions(String... locations) throws BeansException {
        for (String location : locations) {
            loadBeanDefinitions(location);
        }
    }


    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");

            //增加对init-method、destroy-method的读取
            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中
            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);
        }
    }

}

在XmlBeanDefinitionReader中的doLoadBeanDefinitions方法里,主要添加了

//增加对init-method、destroy-method的读取
String initMethod = bean.getAttribute("init-method");
String destroyMethodName = bean.getAttribute("destroy-method");
//额外设置到beanDefinition中
beanDefinition.setInitMethodName(initMethod);
beanDefinition.setDestroyMethodName(destroyMethodName);

增加对新增属性的读取,并添加到BeanDefinition中


3、执行 Bean 对象的初始化方法

public abstract class  AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {

    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    /**
     * 创建bean
     * @param beanName
     * @param beanDefinition
     * @param args
     * @return
     * @throws BeansException
     */
    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
        Object bean = null;
        try {
            bean = createBeanInstance(beanName,beanDefinition,args);
            // 属性填充
            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;
    }
    
	
    private void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
        if (bean instanceof DisposableBean || StringUtils.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(bean, beanName);
        return wrappedBean;
    }


    // 初始化bean
    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 (StringUtils.isNoneEmpty(initMethodName)) {
            Method initMehtod = beanDefinition.getBeanClass().getMethod(initMethodName);
            if (initMehtod == null) {
                throw new BeansException("Could not find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'");
            }
            initMehtod.invoke(bean);
        }

    }


    /**
     * 创建实例
     * @param beanName
     * @param beanDefinition
     * @param args
     * @return
     */
    protected Object createBeanInstance(String beanName, BeanDefinition  beanDefinition, Object[] args) {
        Constructor constructorToUse  = null;
        Class beanClass = beanDefinition.getBeanClass();
        Constructor[] declaredConstructors = beanClass.getDeclaredConstructors();
        for (Constructor ctor : declaredConstructors) {
            if (args != null && ctor.getParameterTypes().length == args.length) {
                constructorToUse  = ctor;
                break;
            }
        }
        return getInstantiationStrategy().instantiate(beanDefinition,beanName,constructorToUse,args);
    }


    /**
     * 属性填充
     * @param beanName
     * @param bean
     * @param beanDefinition
     */
    protected void applyPropertyvalues(String beanName, Object bean,BeanDefinition beanDefinition) {
        try {
            PropertyValues propertyValues = beanDefinition.getPropertyValues();
            for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
                String name = propertyValue.getName();
                Object value = propertyValue.getValue();

                if (value instanceof BeanReference) {
                    // 获取 依赖的对象实例化
                    BeanReference beanReference = (BeanReference) value;
                    value = getBean(beanReference.getBeanName());
                }
                BeanUtil.setFieldValue(bean, name, value);
            }
        } catch (BeansException e) {
            throw new BeansException("Error setting property values:" + beanName);
        }
    }

    /**
     * 获取实例化策略
     * @return
     */
    public InstantiationStrategy getInstantiationStrategy() {
        return instantiationStrategy;
    }

    // 定义实例化策略
    public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
        this.instantiationStrategy = instantiationStrategy;
    }


    @Override
    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessBeforeInitialization(result, beanName);
            if (null == current) return result;
            result = current;
        }
        return result;
    }

    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(result, beanName);
            if (null == current) return result;
            result = current;
        }
        return result;
    }
}

1、抽象类 AbstractAutowireCapableBeanFactory 中的 createBean 是用来创建 Bean 对象的方法,在这个方法中我们之前已经扩展了 BeanFactoryPostProcessor、BeanPostProcessor 操作,这里我们继续完善执行 Bean 对象的初始化方法的处理动作。

2、在方法 invokeInitMethods 中,主要分为两块来执行

 2.1、实现了 InitializingBean 接口的操作,处理 afterPropertiesSet 方法。
 2.2、另外一个是判断配置信息 init-method 是否存在,执行反射调用 initMethod.invoke(bean)。

这两种方式都可以在 Bean 对象初始化过程中进行处理加载 Bean 对象中的初始化操作,让使用者可以额外新增加自己想要的动作


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

/**
 * 定义销毁方法适配器(接口和配置)
 */
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 (destroyMethod == null) {
                throw new BeansException("Couldn't find a destroy method named '" + destroyMethodName + "' on bean with name '" + beanName + "'");
            }
            destroyMethod.invoke(bean);
        }
    }
}

1、思考?为什么这里怎么有一个适配器的类呢,因为销毁方法有两种甚至多种方式,目前有实现接口 DisposableBean、配置信息 destroy-method,两种方式。

2、而这两种方式的销毁动作是由 AbstractApplicationContext 在注册虚拟机钩子后,虚拟机关闭前执行的操作动作。
那么在销毁执行时不太希望还得关注都销毁那些类型的方法,它的使用上更希望是有一个统一的接口进行销毁,所以这里就新增了适配类,做统一处理。


5、定义注册销毁对象、销毁实现

public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

    /**
     * @desc: 单例池
     * @author: ljc
     * @date: 2022/12/7 10:57
     * @param:
     * @return:
     **/
    private Map<String,Object> singletonObjects = new HashMap<>();

    /**
     * 销毁bean
     */
    private final Map<String, DisposableBean> disposableBeans = new HashMap<>();


    /**
     * @desc: 获取单例bean
     * @author: ljc
     * @date: 2022/12/7 10:57
     * @param: [beanName]
     * @return: java.lang.Object
     **/
    @Override
    public Object getSingleton(String beanName) {
        return singletonObjects.get(beanName);
    }

    /**
     * @desc: 入单例池
     * @author: ljc
     * @date: 2022/12/7 10:56
     * @param: [beanName, singletonObject]
     * @return: void
     **/
    protected void addSingleton(String beanName,Object singletonObject){
        singletonObjects.put(beanName,singletonObject);
    }


    /**
     * 销毁对象注册(在对象初始化后,实现了销毁接口的bean对象,会注册)
     * @param beanName
     * @param bean
     */
    public void registerDisposableBean(String beanName, DisposableBean bean) {
        disposableBeans.put(beanName, bean);
    }

    /**
     * 注册过的需要销毁的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);
            }
        }
    }
}

在DefaultSingletonBeanRegistry 中,新增了registerDisposableBean和destroySingletons方法
1、registerDisposableBean:提供了注册方法(用于bean初始化后,实现了销毁接口的bean对象,进行注册)
2、destroySingletons:提供应用上下文关闭时,调用的销毁方法


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

public abstract class  AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {

    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    /**
     * 创建bean
     * @param beanName
     * @param beanDefinition
     * @param args
     * @return
     * @throws BeansException
     */
    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
        Object bean = null;
        try {
            bean = createBeanInstance(beanName,beanDefinition,args);
            // 属性填充
            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;
    }

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

1、在AbstractAutowireCapableBeanFactory 的createBean方法中,添加了

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

销毁bean对象的注册方法,把销毁方法保存起来**,方便后续执行销毁动作进行调用。

2、那么这个销毁方法的具体方法信息,会被注册到 DefaultSingletonBeanRegistry 中新增加的 Map<String, DisposableBean> disposableBeans 属性中去,因为这个接口的方法最终可以被类 AbstractApplicationContext 的 close 方法通过
getBeanFactory().destroySingletons() 调用。

3、在注册销毁方法的时候,会根据是接口类型和配置类型统一交给 DisposableBeanAdapter 销毁适配器类来做统一处理。实现了某个接口的类可以被 instanceof 判断或者强转后调用接口方法


7、虚拟机关闭钩子注册调用销毁方法

public interface ConfigurableApplicationContext extends ApplicationContext {

    /**
     * 刷新容器
     *
     * @throws BeansException
     */
    void refresh() throws BeansException;

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

    /**
     * 手动执行关闭的方法
     */
    void close();
}

在ConfigurableApplicationContext接口中,定义注册虚拟机钩子的方法 registerShutdownHook 和手动执行关闭的方法 close

/**
 * 应用上下文抽象类实现
 */
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {


    /**
     * 添加钩子方法close
     */
    @Override
    public void registerShutdownHook() {
        Runtime.getRuntime().addShutdownHook(new Thread(this::close));
    }


    /**
     * 调用关闭方法时,会执行销毁的自定义处理
     */
    @Override
    public void close() {
        getBeanFactory().destroySingletons();
    }

    /**
     * @desc: 刷新容器
     **/
    @Override
    public void refresh() throws BeansException {
        // 1、创建BeanFactory,并加载BeanDefintion
        refreshBeanFactory();

        // 2、获取beanFactory
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();

        // 3、在 Bean 实例化之前,执行 BeanFactoryPostProcesso
        invokeBeanFactoryPostProcessors(beanFactory);

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

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


    /**
     * @desc: 实例化bean工厂&获取、加载资源&注册Bean定义
     **/
    protected abstract void refreshBeanFactory() throws BeansException;

    protected abstract ConfigurableListableBeanFactory getBeanFactory();

    /**
     * @desc: bean定义加载后,bean实例化前(对bean定义的扩展)
     **/
    private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
        for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessorMap.values()) {
            beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
        }
    }

    /**
     * @desc: 注册,初始化bean扩展点
     **/
    private void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        Map<String, BeanPostProcessor> beanPostProcessorMap = beanFactory.getBeansOfType(BeanPostProcessor.class);
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorMap.values()) {
            beanFactory.addBeanPostProcessor(beanPostProcessor);
        }
    }

    /**
     * @desc: 根据类型获取相同类型的map集合
     **/
    @Override
    public <T> Map<String, T> getBeansOfType(Class<T> type) throws org.springframework.beans.BeansException {
        return getBeanFactory().getBeansOfType(type);
    }

    @Override
    public String[] getBeanDefinitionNames() {
        return getBeanFactory().getBeanDefinitionNames();
    }

    @Override
    public Object getBean(String name) throws BeansException {
        return getBeanFactory().getBean(name);
    }

    @Override
    public Object getBean(String name, Object... args) throws BeansException {
        return getBeanFactory().getBean(name, args);
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return getBeanFactory().getBean(name, requiredType);
    }

}

在AbstractApplicationContext抽象类中,实现了registerShutdownHook和close方法,这里主要体现了关于注册钩子和关闭的方法实现
1、registerShutdownHook:注册钩子方法也就是,注册close方法。(在一些中间件和监控系统的设计中也可以用得到,比如监测服务器宕机,执行备机启动操作)

2、close:调用销毁方法,执行定义的销毁逻辑

二、测试

1、事先准备

public class UserDao {

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

    public void initDataMethod(){
        System.out.println("UserDao xml的实现, 执行:init-method");
        hashMap.put("10001", "ljc");
        hashMap.put("10002", "yaya");
        hashMap.put("10003", "zz");
    }

    public void destroyDataMethod(){
        System.out.println("UserDao xml的实现, 执行:destroy-method");
        hashMap.clear();
    }

    public String queryUserName(String uId) {
        return hashMap.get(uId);
    }

}

UserDao定义了销毁destroyDataMethod和初始化initDataMethod方法,用于xml的属性指定,最终反射完成调用

public class UserService implements InitializingBean, DisposableBean {

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

    public String queryUserInfo() {
        return userDao.queryUserName(uId)+", 公司:"+company+", 地点"+location;
    }


    @Override
    public void destroy() throws Exception {
        System.out.println("以实现接口的形式,执行:UserService.destroy");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("以实现接口的形式,执行:UserService.afterPropertiesSet");
    }

    public String getuId() {
        return uId;
    }

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

    public UserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    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;
    }


}

UserService,以实现接口 InitializingBean, DisposableBean 的两个方法, destroy()、afterPropertiesSet(),处理相应的初始化和销毁方法的动作。在属性设置后执行(afterPropertiesSet,方法名字很好)


2、配置文件

spring.xml文件名,基础配置,无BeanFactoryPostProcessor、BeanPostProcessor,实现类

<?xml version="1.0" encoding="UTF-8"?>
<beans>

    <bean id="userDao" class="springframework.test.bean.UserDao" init-method="initDataMethod" destroy-method="destroyDataMethod"/>

    <bean id="userService" class="springframework.test.bean.UserService">
        <property name="uId" value="10001"/>
        <property name="company" value="腾讯"/>
        <property name="location" value="深圳"/>
        <property name="userDao" ref="userDao"/>
    </bean>

</beans>

配置文件中主要是userDao新增了,init-method=“initDataMethod” destroy-method=“destroyDataMethod”,这样两个配置。从源码的学习中可以知道,这两个配置是为了加入到 BeanDefinition 定义类之后写入到类 DefaultListableBeanFactory 中的 beanDefinitionMap 属性中去的。


3、单元测试

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

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

测试方法中新增加了一个,注册钩子的动作。applicationContext.registerShutdownHook();

测试结果

UserDao xml的实现, 执行:init-method
以实现接口的形式,执行:UserService.afterPropertiesSet
测试结果:ljc, 公司:腾讯, 地点深圳
以实现接口的形式,执行:UserService.destroy
UserDao xml的实现, 执行:destroy-method

Process finished with exit code 0

从测试结果可以看到,我们的新增加的初始和销毁方法已经可以如期输出结果了。

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

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

相关文章

UNIAPP实战项目笔记55 自定义Tabbar并使用Tabbar事件拦截未登录跳转到登录页面

UNIAPP实战项目笔记55 自定义Tabbar并使用Tabbar拦截未登录跳转到登录页面 点击购物车和我的的时候需要拦截并验证登录 通过验证的直接跳转,为通过验证的跳转到登录页面 通过自定义tabbar来实现 实际案例图片 正常跳转 拦截跳转 代码 pages.json 页面配置 {"pages&qu…

JDBC编程相关知识(实现图书管理系统进阶版)

目录 一、配置MySQL数据库驱动包 二、JDBC常规操作 1、创建数据源 2、建立连接 3、操作数据库&#xff0c;执行sql语句 4、关闭资源 三、JDBC实现图书管理系统 1、建表 2、连接数据库 3、创建实体类 a、Book类 b、BookShelf类 c、User类 d、Administrator类 e、…

jsp+ssm计算机毕业设计大学生足球预约信息【附源码】

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JSPSSM mybatis Maven等等组成&#xff0c;B/S模式 Mave…

java计算机毕业设计springboot+vue青少年编程在线考试系统

项目介绍 21世纪的今天,随着社会的不断发展与进步,人们对于信息科学化的认识,已由低层次向高层次发展,由原来的感性认识向理性认识提高,管理工作的重要性已逐渐被人们所认识,科学化的管理,使信息存储达到准确、快速、完善,并能提高工作管理效率,促进其发展。 论文主要是对青少…

AIGC stable diffusion完整部署手册 for linux

准备工作 &#xff08; GPU服务器4G显卡 &#xff09;&#xff1a; 1. 建议 Python3.10.7 以上的版本&#xff0c;最低不能低于3.10.5 2. pip pip3的版本升级到最新 pip 22.3.1 3. 安装python3.10.7之前先安装和升级 openssl-1.1.1q 4. yum安装源改成 阿里云 或者 清华 …

English Learning - L1-4 从此没有不会的表达(下) 2022.12.15 周四

English Learning - L1-4 从此没有不会的表达&#xff08;下&#xff09; 2022.12.15 周四5 动名词5.1 动名词都有什么作用作主语做表语做定语动名词做定语有啥讲究&#xff1f;做宾语5.2 动名词的复合结构---成功加入动名词自己的主语逻辑主语动名词复合结构作宾语时用普通格和…

2021年度汇总丨20大热门TWS耳机功能配置汇总解析

疫情下的时间匆匆而过&#xff0c;2021已成为过去&#xff0c;2022年正式开启。在过去的一年中&#xff0c;以TWS耳机为代表的音频市场依旧保持着健康稳定的发展&#xff0c;彰显这一市场超强的活力和朝好的未来发展。 回顾2021&#xff0c;这一年中&#xff0c;我爱音频网365天…

直线检测霍夫变换VS深度学习

1. 引言 直线检测有很多应用&#xff1a;例如&#xff0c;创建矢量化图、端点检测等。在这篇文章中&#xff0c;我们不讨论其具体的应用程序&#xff0c;而是重点关于于如何使用传统算法和深度学习的方法来进行直线检测&#xff0c;这里我们将对二者进行相关比较。 闲话少说&a…

经典算法之异或运算(无进位相加)

目录异或运算的定义异或运算的性质异或运算的应用交换两数翻转指定位寻找单身狗异或运算的定义 众所周知&#xff0c;计算机中的所有数据都是以二进制&#xff08;0或者1&#xff09;的形式存储。而异或运算符&#xff08;^&#xff09;就是将参加运算的两个数据&#xff0c;按…

C语言—数据类型

通过sizeof运算符获取数据类型的大小&#xff0c;int占4个字节&#xff0c;表示一个int变量可以存储32位的数据&#xff1b; vs2022中&#xff0c;sizeof运算符返回的结果是一个64位 (8bytes)的无符号整型的值&#xff0c;所以占位符要使用%lld&#xff1b; #include <std…

【空气起动系统控制】使用PID调节起动机入口压力值

前段时间师傅让做一个空起系统的建模&#xff0c;结果给忘了&#xff0c;现在趁着这几天疫情封控期间在家搞一搞&#xff0c;断断续续搞了五天&#xff0c;空起系统控制的相关资料太少了&#xff0c;还好最后弄出来了&#xff0c;虽然最后还存在点小问题&#xff0c;不过大体上…

【Java编程】SSH:Struts、Spring、Hibernate

SSH&#xff1a;Struts、Spring、HibernateSSH 框架指的是 Struts、Spring 和 Hibernate 三者的集成。集成 SSH 框架的系统从职责上分为表示层、业务逻辑层、数据持久层和域模块层。Struts 属于 MVC 框架的一种&#xff0c;提供了对 MVC 系统底层的支持&#xff0c;负责 MVC 的…

汽车ECU通信相关验证项有哪些?

已剪辑自: https://mp.weixin.qq.com/s/-fIAXkS37r6jvnuA7yIQDA 汽车电子的高速发展决定了基础软件所面临的要求将会更加严格&#xff0c;其要求会覆盖软件的安全性、稳定性、可扩展性等方方面面。为了提高软件质量&#xff0c;降低软件应用风险&#xff0c;构建高安全、高可靠…

什么是网站安全?如何查询网站在百度是否安全?

什么是网站安全&#xff1f; 百度词条的定义为网站安全是指出于防止网站受到黑客入侵者对其网站进行挂马&#xff0c;篡改网站源代码&#xff0c;被窃取数据等行为而做出一系列的安全防御工作&#xff0c;在我的理解中&#xff0c;网站安全就是当有人攻击你的网站时&#x…

Java+SSM社团管理系统(含源码+论文+答辩PPT等)

项目功能简介: 该项目采用的技术实现如下 后台框架&#xff1a;Spring、SpringMVC、MyBatis UI界面&#xff1a;JSP、jQuery 、H-ui 数据库&#xff1a;MySQL 系统功能 本社团管理系统是根据大学社团管理的实际应用而开发的&#xff0c;采用JSP技术&#xff0c;Java编程语言&am…

python调用halcon程序/.hdev文件/直接调用halcon引擎

一、前言 python有两种调用halcon的方法 1、安装halcon的python库&#xff0c;pip install mvtec-halcon20111 然后python代码中主要调用方式为&#xff1a;通过ha来调用halcon中的各种算子&#xff0c;本人不是很喜欢这种方式&#xff0c;后面主要介绍第2种方法。 import …

java计算机毕业设计springboot+vue校园出入管理系统

项目介绍 本论文主要论述了如何使用JAVA语言开发一个校园出入管理系统 ,本系统将严格按照软件开发流程进行各个阶段的工作,采用B/S架构,面向对象编程思想进行项目开发。在引言中,作者将论述校园出入管理系统的当前背景以及系统开发的目的,后续章节将严格按照软件开发流程,对系…

可能你看到的大部分教材里讲的指针和指针变量是一个概念,但是真的是这样吗?看完我这篇文章肯定会颠覆你的认知哦?

指针vs指针变量一、指针和指针变量的含义1、变量的“左值”和“右值”2、什么是指针&#xff1f;3、什么是指针变量&#xff1f;二、如何看待“指针就是指针变量”这种说法三、为什么要存在指针&#xff1f;四、对指针进行解引用操作的本质一、指针和指针变量的含义 1、变量的…

2022云南省职业院校技能大赛(高职组) Web技术(网站设计与开发)赛项规程

2022云南省职业院校技能大赛&#xff08;高职组&#xff09; Web技术&#xff08;网站设计与开发&#xff09;赛项规程 一、赛项名称 赛项名称&#xff1a;Web技术&#xff08;网站设计与开发&#xff09; 赛项组别&#xff1a;高职组 二、竞赛内容 Web技术&#xff08;网站设计…

SA-UNet:用于视网膜血管分割的空间注意力UNet

摘要 在本文中提出了一种名为空间注意力UNet&#xff08;SA-UNet&#xff09;的轻量级网络&#xff0c;它不需要大量的带注释的训练样本&#xff0c;可以以数据增强的方式来更有效的使用可用的带注释的样本。SA-UNet引入一种空间注意模块&#xff0c;这个模块沿着空间维度推断…