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

文章目录

  • 一、目标:初始化和销毁方法
  • 二、设计:初始化和销毁方法
  • 三、实现:初始化和销毁方法
    • 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();}@Overridepublic void destroy() throws Exception {// 1.实现接口 DisposableBeanif (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 > nameString beanName = StrUtil.isNotEmpty(id) ? id : name;if (StrUtil.isEmpty(beanName)) {beanName = StrUtil.lowerFirst(clazz.getSimpleName());}// 定义beanBeanDefinition 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;}// 解析标签:propertyElement 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");}// 注册 BeanDefinitiongetRegistry().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<>();@Overridepublic Object getSingleton(String beanName) {return singletonObjects.get(beanName);}@Overridepublic 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();@Overrideprotected 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.实现接口 InitializingBeanif (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 {@Overridepublic void refresh() throws BeansException {// 1.创建 BeanFactory,并加载 BeanDefinitionrefreshBeanFactory();// 2.获取 BeanFactoryConfigurableListableBeanFactory beanFactory = getBeanFactory();// 3.在 Bean 实例化之前,执行 BeanFactoryPostProcessinvokeBeanFactoryPostProcessor(beanFactory);// 4.BeanPostProcessor 需要提前与其他 Bean 对象实例化之前执行注册操作registerBeanPostProcessor(beanFactory);// 5.提前实例化单例 Bean 对象beanFactory.preInstantiateSingletons();}@Overridepublic void registerShutdownHook() {Runtime.getRuntime().addShutdownHook(new Thread(this::close));}@Overridepublic 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;@Overridepublic void destroy() throws Exception {System.out.println("执行:UserService.destroy");}@Overridepublic 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.初始化 BeanFactoryClassPathXmlApplicationContext 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.mzph.cn/news/70841.shtml

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

相关文章

机器学习基础算法--回归类型和评价分析

目录 1.数据归一化处理 2.数据标准化处理 3.Lasso回归模型 4.岭回归模型 5.评价指标计算 1.数据归一化处理 """ x的归一化的方法还是比较多的我们就选取最为基本的归一化方法 x(x-x_min)/(x_max-x_min) """ import numpy as np from sklea…

CSS构建基础(二)选择器

在CSS中&#xff0c;选择器用于定位我们想要样式化的网页上的HTML元素。有各种各样的CSS选择器可用&#xff0c;允许在选择要样式化的元素时实现细粒度的精度。在本文及其子文章中&#xff0c;我们将详细介绍不同的类型&#xff0c;了解它们是如何工作的。 1、什么是选择器? …

浏览器跨域

相关问题 什么是跨域为什么会跨域为什么会有跨域的限制怎么解决跨域 回答关键点 CORS和同源策略 跨域问题的来源是浏览器为了请求安全而引入的基于同源策略的安全特性。当页面和请求的协议、主机名或端口不同时&#xff0c;浏览器判定两者不同源&#xff0c;即为跨域请求。需…

PyTorch 深度学习实践 第10讲刘二大人

总结&#xff1a; 1.输入通道个数 等于 卷积核通道个数 2.卷积核个数 等于 输出通道个数 1.单通道卷积 以单通道卷积为例&#xff0c;输入为&#xff08;1,5,5&#xff09;&#xff0c;分别表示1个通道&#xff0c;宽为5&#xff0c;高为5。假设卷积核大小为3x3&#xff0c…

定时任务实现方案总结

一、概述 定时任务的作用是在设定的时间和日期后自动执行任务&#xff0c;执行任务的周期既能是单次也能是周期性。 本文重点说明Timer、ScheduledThreadPoolExecutor、Spring Task、Quartz等几种定时任务技术方案。 二、Timer JDK自带的Timer是最古老的定时任务实现方式了。…

博物馆网上展厅有哪些用途,如何搭建数字时代的文化宝库

引言&#xff1a; 博物馆一直以来都是保存、展示和传承人类文化遗产的地方。然而&#xff0c;随着数字时代的来临&#xff0c;博物馆也逐渐迎来了创新的变革。博物馆网上展厅&#xff0c;作为一种新型的文化传播方式&#xff0c;正逐渐崭露头角。 一.什么是博物馆网上展厅&…

【目标检测】理论篇(3)YOLOv5实现

Yolov5网络构架实现 import torch import torch.nn as nnclass SiLU(nn.Module):staticmethoddef forward(x):return x * torch.sigmoid(x)def autopad(k, pNone):if p is None:p k // 2 if isinstance(k, int) else [x // 2 for x in k] return pclass Focus(nn.Module):def …

《C++设计模式》——结构型

前言 结构模式可以让我们把很多小的东西通过结构模式组合起来成为一个打的结构&#xff0c;但是又不影响各自的独立性&#xff0c;尽可能减少各组件之间的耦合。 Adapter Class/Object(适配器&#xff09; Bridge(桥接&#xff09; Composite(组合) Decorator(装饰) 动态…

【5】openGL使用宏和函数进行错误检测

当我们编写openGL程序&#xff0c;没有报编译链接错误&#xff0c;但是运行结果是黑屏&#xff0c;这不是我们想要的。 openGL提供了glGetError 来检查错误&#xff0c;我们可以通过在运行时进行打断点查看glGetError返回值&#xff0c;得到的是一个十进制数&#xff0c;将其转…

C++(Liunx) 使用cut截 取出Ubuntu用户的家目录,要求:不能使用“:“作为分割.

使用cut截 取出Ubuntu用户的家目录&#xff0c;要求&#xff1a;不能使用":"作为分割

【C++技能树】多态解析

Halo&#xff0c;这里是Ppeua。平时主要更新C&#xff0c;数据结构算法&#xff0c;Linux与ROS…感兴趣就关注我bua&#xff01; 文章目录 0.多态的概念0.1 多态的定义 1. 重写2.Final与Override3.抽象类4.多态中的内存分布.4.1虚表存在哪里? 5.多态调用原理5.1 动态绑定与静…

《向量数据库指南》——AI原生向量数据库Milvus Cloud 2.3 Enhancement

Enhancement MMap 技术提升数据容量 MMap 是 Linux 内核提供的技术,可以将一块磁盘空间映射到内存,这样一来我们便可以通过将数据加载到本地磁盘再将磁盘 mmap 到内存的方案提升单机数据的容量,经过测试使用 MMap 技术后数据容量提升了 1 倍而性能下降在20% 以内,大大节约了…

sqlibs安装及复现

sqlibs安装 安装phpstudy后&#xff0c;到github上获取sqlibs源码 sqli-labs项目地址—Github获取&#xff1a;GitHub - Audi-1/sqli-labs: SQLI labs to test error based, Blind boolean based, Time based. 在phpstudy本地文件中的Apache目录中解压上方下载的源码。 将sq…

08.SCA-CNN

目录 前言泛读摘要IntroductionRelated Work 精读Spatial and Channel-wise Attention CNNOverviewSpatial AttentionChannel-wise AttentionChannel-SpatialSpatial-Channel ExperimentsDataset and Metric设置 评估Channel-wise Attention&#xff08;问题1&#xff09;评估M…

momentjs实现DatePicker时间禁用

momentjs是一个处理时间的js库&#xff0c;简洁易用。 浅析一下&#xff0c; momentjs 在vue中对DatePicker时间组件的禁用实践。 一&#xff0c;npm下载 npm install moment --save二&#xff0c;particles.json中 "dependencies": {"axios": "^…

单片机第三季-第一课:STM32基础

官方网址&#xff1a;STMCU中文官网 STM32系列分类&#xff1a; 型号命名原则&#xff1a; STM32F103系列&#xff1a; 涉及到的几个概念&#xff1a; DMA&#xff1a;Direct Memory Access&#xff0c;直接存储器访问。DMA传输将数据从一个地址空间复制到另一个地址空间&…

系统学习Linux-zabbix监控平台

一、zabbix的基本概述 zabbix是一个监控软件&#xff0c;其可以监控各种网络参数&#xff0c;保证企业服务架构安全运营&#xff0c;同时支持灵活的告警机制&#xff0c;可以使得运维人员快速定位故障、解决问题。zabbix支持分布式功能&#xff0c;支持复杂架构下的监控解决方…

DataTable扩展 列转行方法(2*2矩阵转换)

源数据 如图所示 // <summary>/// DataTable扩展 列转行方法&#xff08;2*2矩阵转换&#xff09;/// </summary>/// <param name"dtSource">数据源</param>/// <param name"columnFilter">逗号分隔 如SDateTime,PM25,PM10…

【QT】使用qml的QtWebEngine遇到的一些问题总结

在使用qt官方的一些QML的QtWebEngine相关的例程的时候&#xff0c;有时在运行会报如下错误&#xff1a; WebEngineContext used before QtWebEngine::initialize() or OpenGL context creation failed 这个问题在main函数里面最前面加上&#xff1a; QCoreApplication::setAttr…

Linux下的系统编程——认识进程(七)

前言&#xff1a; 程序是指储存在外部存储(如硬盘)的一个可执行文件, 而进程是指处于执行期间的程序, 进程包括 代码段(text section) 和 数据段(data section), 除了代码段和数据段外, 进程一般还包含打开的文件, 要处理的信号和CPU上下文等等.下面让我们开始对Linux进程有个…