文章目录
- 一、目标:初始化和销毁方法
- 二、设计:初始化和销毁方法
- 三、实现:初始化和销毁方法
- 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 时用到的
InitializingBean
、DisposableBean
两个接口。
- 如:使用 Spring 时用到的
二、设计:初始化和销毁方法
💡 技术设计:spring.xml配置文件配置初始化和销毁方法
- 对于 这样在 Bean 容器初始化过程中额外添加的处理操作,就是预先执行了一个定义好的接口或者是反射调用类中 xml 中配置的方法。
- 最终只要按照接口定义实现,就会有 Spring 容器在处理的过程中进行调用即可。
- 在
spring.xml
配置中添加init-method
、destroy-method
两个注解,在配置文件加载的过程中,把注解配置一并定义到 BeanDefinition 的属性当中。- 这样在
initializeBean
初始化操作的工程中,就可以通过发射的方式来调用配置在 Bean 定义属性当中的方法信息了。 - 另外如果是接口实现的方式,那么直接可以通过 Bean 对象调用对应接口定义的方法即可。
((InitializingBean) bean).afterPropertiesSet()
,两种方式的效果是一样的。
- 这样在
- 除了在初始化的操作外,
destroy-method
和DisposableBean
接口的定义,都会在 Bean 对象初始化完成阶段,执行注册销毁方法的信息到DefaultSingletonBeanRegistry
类中的disposableBeans
属性里,这里为了后续统一操作。- 适配器模式:因为反射调用和接口直接调用,是两种方式,所以需要使用适配器进行包装。
- 参考
DisposableBeanAdapter
的具体实现:销毁方法需要在虚拟机执行关闭之前进行操作,所以这里需要用到一个注册钩子的操作。- 如:
Runtime.getRuntime().addShutdownHook(new Thread(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 配置和定义接口。
- 定义接口:
InitializingBean
、DisposableBean
。 - xml 配置:
XmlBeanDefinitionReader
加载 spring.xml 配置信息到 BeanDefinition 中。
- 定义接口:
- 接口
ConfigurableBeanFactory
定义了destroySingletons
方法。- 把实现接口的操作又交给继承的父类处理,是一种隔离分层服务的设计方式。
- 最后就是向虚拟机注册钩子,保证在虚拟机关闭之前,执行销毁操作。
Runtime.getRuntime().addShutdownHook(new Thread(this::close))
3.3 定义初始化和销毁方法的接口
InitializingBean
、DisposableBean
,两个接口方法都是比较常用的,在结合一些 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
新增加了两个属性:initMethodName
、destroyMethodName
。- 这两个属性是为了在
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 对象的方法,在这个方法中我们已经扩展了BeanFactoryPostProcessor
、BeanPostProcessor
操作,现在继续完善执行 Bean 对象的初始化方法的处理动作。 - 在方法
invokeInitMethods
中,主要分两块来执行。- 实现了
InitializingBean
接口的操作,处理afterPropertiesSet
方法。 - 另外一个是判断配置信息
init-method
是否存在,执行反射调用initMethod.invoke(bean)
。 - 这两种方式都可以在 Bean 对象初始化过程中进行处理加载 Bean 对象中的初始化操作,让使用者可以额外新增加自己想要的动作。
- 实现了
创建 Bean 时注册销毁方法对象
- 在创建 Bean 对象的实例的时候,需要把销毁方法保存起来,方便后续执行销毁动作进行调用。
- 那么这个销毁方法的具体方法信息,会被注册到
DefaultSingletonBeanRegistry
中新增加的Map<String, DisposableBean> disposableBeans
属性中去。- 因为这个接口的方法最终可以被类
AbstractApplicationContext
的close
方法通过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;
}
}
- 实现接口
InitializingBean
、DisposableBean
的两个方法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
完成销毁动作的具体实现过程。