目录
1.工程目录
2.xml类图
3.代码实现
3.1 xml方式配置初始化和销毁方法实现
3.2 接口方式定义初始化和销毁方法
这一节比上一节轻松,主要关注的就是可以在操作Bean的过程中去调用用户自定义的初始方法和结束方法,这样用户就可在bean实例化后的时机做自定义初始化的业务逻辑,也可在整个Bean操作完成后进行销毁操作的自定义业务逻辑方法,和上节的自定义修改Bean对象是一个意思
本章代码地址:GitHub - dufGIT/spring-sourcecode
目录为:bean-init-destory
就像在xml中配置了init-method以及destroy-method后就会在Bean一系列中去操作对应的方法,所以第一知道要改动的就是需要在bean定义中(BeanDefinition)添加两个属性,initMethodName,destroyMethodName,这样才能够知道xml中的初始化方法以及销毁方法要调用的是什么方法名。
// 初始化方法名称
private String initMethodName;
// 销毁方法名称
private String destroyMethodName;
除此之外我们bean是定义完了,但是怎么放入beanDefinition里后供系统使用呢,所以在bean加载解析时还要加入如下操作
// 从xml解析出来需要初始化调用的方法名称设置
beanDefination.setInitMethodName(initMethod);
// 从xml解析出来需要调用销毁方法名称的设置
beanDefination.setDestroyMethodName(destroyMethodName);
此外,因为是从xml中解析出来的方法名称,如果真的调用方法还要借助反射,通过beanDefinitin里的beanClass对象传递方法名来获取方法,再调用invoke的操作来实现调用初始化或自定义销毁方法。
1.工程目录
├─src
│ ├─main
│ │ ├─java
│ │ │ └─com
│ │ │ └─spring
│ │ │ └─sourcecode
│ │ │ │ SourcecodeApplication.java
│ │ │ │
│ │ │ └─springframework
│ │ │ │ BeanDefinition.java
│ │ │ │ BeanFactory.java
│ │ │ │
│ │ │ ├─beans
│ │ │ │ │ BeansException.java
│ │ │ │ │ PropertyValue.java
│ │ │ │ │ PropertyValues.java
│ │ │ │ │
│ │ │ │ └─factory
│ │ │ │ │ BeanFactory.java
│ │ │ │ │ ConfigurableListableBeanFactory.java
│ │ │ │ │ DisposableBean.java
│ │ │ │ │ HierarchicalBeanFactory.java
│ │ │ │ │ InitializingBean.java
│ │ │ │ │ ListableBeanFactory.java
│ │ │ │ │
│ │ │ │ ├─config
│ │ │ │ │ AutowireCapableBeanFactory.java
│ │ │ │ │ BeanDefinition.java
│ │ │ │ │ BeanFactoryPostProcessor.java
│ │ │ │ │ BeanPostProcessor.java
│ │ │ │ │ BeanReference.java
│ │ │ │ │ ConfigurableBeanFactory.java
│ │ │ │ │ SingletonBeanRegistry.java
│ │ │ │ │
│ │ │ │ ├─support
│ │ │ │ │ AbstractAutowireCapableBeanFactory.java
│ │ │ │ │ AbstractBeanDefinitionReader.java
│ │ │ │ │ AbstractBeanFactory.java
│ │ │ │ │ BeanDefinitionReader.java
│ │ │ │ │ BeanDefinitionRegistry.java
│ │ │ │ │ CglibSubclassingInstantiationStrategy.java
│ │ │ │ │ DefaultListableBeanFactory.java
│ │ │ │ │ DefaultSingletonBeanRegistry.java
│ │ │ │ │ DisposableBeanAdapter.java
│ │ │ │ │ InstantiationStrategy.java
│ │ │ │ │ SimpleInstantiationStrategy.java
│ │ │ │ │
│ │ │ │ └─xml
│ │ │ │ XmlBeanDefinitionReader.java
│ │ │ │
│ │ │ ├─context
│ │ │ │ │ ApplicationContext.java
│ │ │ │ │ ConfigurableApplicationContext.java
│ │ │ │ │
│ │ │ │ └─supprot
│ │ │ │ AbstractApplicationContext.java
│ │ │ │ AbstractRefreshableApplicationContext.java
│ │ │ │ AbstractXmlApplicationContext.java
│ │ │ │ ClassPathXmlApplicationContext.java
│ │ │ │
│ │ │ ├─core
│ │ │ │ └─io
│ │ │ │ ClassPathResource.java
│ │ │ │ DefaultResourceLoader.java
│ │ │ │ FileSystemResource.java
│ │ │ │ Resource.java
│ │ │ │ ResourceLoader.java
│ │ │ │ UrlResource.java
│ │ │ │
│ │ │ └─util
│ │ │ ClassUtils.java
│ │ │
│ │ └─resources
│ │ application.properties
│ │ spring.xml
│ │
│ └─test
│ └─java
│ └─com
│ └─spring
│ └─sourcecode
│ │ SourcecodeApplicationTests.java
│ │
│ └─springframework
│ │ ApiTest10.java
│ │
│ ├─beanhadel
│ │ MyBeanFactoryPostProcessor.java
│ │ MyBeanPostProcessor.java
│ │
│ └─test
│ UserDao.java
│ UserService.java
2.xml类图
InitializingBean:定义初始化接口
DisposableBean:定义销毁接口
BeanDefiniton:此类主要添加配置文件要处理的事情,如本节的初始化名,销毁名,都需要通过解析放入BeanDefiniton中,然后在Bean处理中获取再进行处理。
上图uml绿色的部分则主要处理调用用户自定义初始化的方法业务逻辑
ConfigurableApplicationContext:定义了registerShutdownHook()和close()方法
AbstractApplicationContext:实现了registerShutdownHook()方法,根据Runtime.getRuntime().addShutdownHook(new Thread(this::close));此行代码将close方法注册到jvm虚拟机上,然后当程序正常执行完毕后,调用之前注册的方法close()
DefaultSingletonBeanRegistry:此类主要是处理销毁容器的,往map容器里注册有要销毁方法的标识,删除map容器里要销毁方法的标识操作。
这里看紫色部分xm图有一个有趣的部分,ConfigurableBeanFactory定义了销毁方法,
而AbstractBeanFactory继承了DefaultSingletonBeanRegistry实现了ConfigurableBeanFactory,却没有处理此章节中的任何销毁内容,而是交给了自己的父类DefaultSingletonBeanRegistry去处理,这样的处理可以使功能分层,可扩展,易维护。
上图xml中紫色的部分主要是处理销毁操作的业务
3.代码实现
beanDefinition的更改
package com.spring.sourcecode.springframework.beans.factory.config;
import com.spring.sourcecode.springframework.beans.PropertyValues;
/**
* @Author df
* @Date 2021/11/8 11:26
* @Version 1.0
*/
// Bean定义
public class BeanDefinition {
// 按上一章的话这里已经把Object改成了Class,这样就可以把bean的实例化操作放到容器中处理了
private Class beanClass;
private PropertyValues propertyValues;
// 初始化方法名称
private String initMethodName;
// 销毁方法名称
private String destroyMethodName;
public BeanDefinition(Class beanClass) {
this.beanClass = beanClass;
// 此处加入new PropertyValues()目的是在Bean对象没有属性时后续获取会报空错,在此处理
this.propertyValues = new PropertyValues();
}
public BeanDefinition(Class beanClass, PropertyValues propertyValues) {
this.beanClass = beanClass;
this.propertyValues = propertyValues != null ? propertyValues : new PropertyValues();
}
// 省略set/get
}
XmlBeanDefinitionReader类里的解析xml的方法中添加如下操作,那么在bean操作的时候就能够获取相关的信息。
String initMethod = bean.getAttribute("init-method");
String destroyMethodName = bean.getAttribute("destroy-method");
// 从xml解析出来需要初始化调用的方法名称设置
beanDefination.setInitMethodName(initMethod);
// 从xml解析出来需要调用销毁方法名称的设置
beanDefination.setDestroyMethodName(destroyMethodName);
首先xml中的数据是可以解析出来可以初始化或销毁的,但是也可以封装成接口的模式,通过接口延迟到自定义子类去实现自定义初始化和销毁操作。
3.1 xml方式配置初始化和销毁方法实现
AbstractAutowireCapableBeanFactory:initializeBean是上节的初始化里的,这节把invokeInitMethods方法里的业务逻辑补充了,主要是调用xml中配置的初始化方法。
@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
Object bean = null;
try {
// 第四节注释调此种获取bean实例方式,因为对于有构造函数的bean会报错
// 获得beanDefination里beanClass的字段的新实例
//bean = beanDefinition.getBeanClass().newInstance();
// 第四节更改
bean = createBeanInstance(beanDefinition, beanName, args);
applyPropertyValues(beanName, bean, beanDefinition);
// 执行bean的初始化方法和BeanPostProcessor的前置和后置处理方法。(都是用户自定义的哦)
bean = initializeBean(beanName, bean, beanDefinition);
} catch (Exception e) {
throw new BeansException("Instantiation of bean failed", e);
}
registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
// 获取bean实例以后添加到单例对象中
addSingleton(beanName, bean);
return bean;
}
private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
// 1. 执行 BeanPostProcessor Before 处理
Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
// 执行自定义初始化方法invokeInitMethods(beanName, wrappedBean, beanDefinition);
try {
invokeInitMethods(beanName, wrappedBean, beanDefinition);
} catch (Exception e) {
e.printStackTrace();
throw new BeansException("Invocation of init method of bean [" + beanName + "] failed", e);
}
// 2. 执行 BeanPostProcessor After 处理
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
return wrappedBean;
}
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefination) {
if (StrUtil.isNotEmpty(beanDefination.getDestroyMethodName())) {
registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefination));
}
}
// 调用用户设置的初始化方法
private void invokeInitMethods(String beanName, Object wrappedBean, BeanDefinition beanDefinition) throws Exception {
// 此用来获取xml里进行的初始化方法的配置 ,最后用invoke反射调用具体的初始化方法。
String initMethodName = beanDefinition.getInitMethodName();
if (StrUtil.isNotEmpty(initMethodName)) {
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(wrappedBean);
}
}
DefaultSingletonBeanRegistry:此类要修改的内容如下,添加一个可以在处理的Bean的过程中就可以解析出来配置文件哪个类里的的方法是可以注销的容器并存储
// 要吊销的bean容器
private final Map<String, DisposableBean> disposableBeans = new HashMap<>();
// 注册销毁容器
public void registerDisposableBean(String beanName, DisposableBean bean) {
disposableBeans.put(beanName, bean);
}
// 销毁方法,从容器取出来解析销毁的方法并调用
public void destroySingletons() {
Set<String> keySet = this.disposableBeans.keySet();
Object[] disposableBeanNames = keySet.toArray();
System.out.println("从容器中取出要销毁的对象进行remove:" + disposableBeanNames.length);
for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
Object beanName = disposableBeanNames[i];
// 将容器中销毁的对象remove
DisposableBean disposableBean = disposableBeans.remove(beanName);
try {
// 调用用户自定义实现的销毁接口
disposableBean.destroy();
} catch (Exception e) {
e.printStackTrace();
throw new BeansException("Destroy method on bean with name '" + beanName + "' threw an exception", e);
}
}
//System.out.println("调用用户自定义实现的销毁接口,disposableBean.destroy()");
}
DisposableBeanAdapter:销毁的处理适配器,专门处理调用自定义销毁方法,利用反射处理。
// 销毁方法适配器,支持接口方式和配置方式
public class DisposableBeanAdapter {
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();
}
public void destroy() throws Exception {
// 此用来将xml中配置的destroyMethodName不为空就进行反射调用,调用xml配置的销毁方法
// 如: destroy-method="destroyDataMethod" 的 destroyDataMethod就是要调用的方法,通过invoke()调用目标方法
if (!(bean instanceof DisposableBean && "destroy".equals(this.destroyMethodName))) {
Method destroyMethod = bean.getClass().getMethod(destroyMethodName);
if (null == destroyMethod) {
throw new BeansException("Couldn't find a destroy method named '" + destroyMethodName + "' on bean with name '" + beanName + "'");
}
System.out.println("通过适配器-反射调用xml中配置的destroyDataMethod方法。");
destroyMethod.invoke(bean);
}
}
}
ConfigurableApplicationContext类上下文处理的操作:需要用户手动调用registerShutdownHook(),此操作是往jvm注册等待程序关闭时执行的方法,那么当程序执行时就会调用相应的方法处理了。
public interface ConfigurableApplicationContext extends ApplicationContext {
/**
* 刷新容器
*
* @throws BeansException
*/
void refresh() throws BeansException;
/**
* 向虚拟机注册钩子,将程序关闭后要调用的方法注册进去
*/
void registerShutdownHook();
/**
* 关闭方法
* */
void close();
}
AbstractApplicationContext:添加如下这两个实现方法
@Override
public void registerShutdownHook() {
System.out.println("进行registerShutdownHook()注册钩子方法!");
// 在程序正常退出或jvm进行关闭时调用传输进来的线程对象方法,此次调用本类里close()方法。
Runtime.getRuntime().addShutdownHook(new Thread(this::close));
}
@Override
public void close() {
System.out.println("程序执行完毕,可以调用close()方法,进行用户自定义销毁工作!");
getBeanFactory().destroySingletons();
}
ConfigurableBeanFactory:添加destroySingletons()的定义销毁方法,让DefaultSingletonBeanRegistry类去实现
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {
void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);
/**
* 销毁
*/
void destroySingletons();
}
userDao:将之前的static静态方法里的数据创建了初始化方法和销毁方法,供Bean对象处理时通过反射调用。
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);
}
}
UserService:
public class UserService {
private String uId;
private String company;
private UserDao userDao;
private String location;
public void queryUserInfo() {
System.out.println("查询用户信息:" + userDao.queryUserName(uId)
+",公司名称:"+company+",地址:"+location);
}
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 getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
}
只处理配置文件的初始化方法和销毁方法的案例,测试一下结果,
@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);
userService.queryUserInfo();
}
结果如下:执行初始化方法销毁方法都已经操作
如上是xml的方式展现,接下来需要在稍微修改一下支持自定义实现接口调用用户的实现的初始化和销毁方法,这个就不需要反射了,直接强转对应的方法接口就可调用对应需要的方法。
3.2 接口方式定义初始化和销毁方法
InitializingBean:初始化Bean的接口,等待实现具体的方法
// 定义初始化接口
public interface InitializingBean {
/*
Bean处理了属性填充后调用
**/
void afterPropertiesSet();
}
定义以后需要实现,那么初始化之前我们通过反射调用xml中的初始化方法已经实现了,那么突破口在这里我们需要判断一下是否是实现了InitializingBean,然后调用具体的方法即可,那么AbstractAutowireCapableBeanFactory类修改invokeInitMethods方法来添加该实现
// 调用用户设置的初始化方法
private void invokeInitMethods(String beanName, Object wrappedBean, BeanDefinition beanDefinition) throws Exception {
// 此用来调用实现InitializingBean,直接调用方法即可
if (wrappedBean instanceof InitializingBean) {
((InitializingBean) wrappedBean).afterPropertiesSet();
}
// 此用来获取xml里进行的初始化方法的配置 ,最后用invoke反射调用具体的初始化方法。
String initMethodName = beanDefinition.getInitMethodName();
if (StrUtil.isNotEmpty(initMethodName)) {
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(wrappedBean);
}
}
定义销毁接口DisposableBean,用来供子类实现自定义销毁方法
// 定义销毁接口
public interface DisposableBean {
void destroy() throws Exception;
}
DisposableBeanAdapter:DisposableBeanAdapter类修改为实现销毁接口DisposableBean,并在destroy()方法里添加是否和DisposableBean有关系的类并强转调用destroy()的方法。
// 销毁方法适配器,支持接口方式和配置方式
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 {
// 此用来实现DisposableBean的接口可直接调用
if (bean instanceof DisposableBean) {
System.out.println("通过适配器-调用实现DisposableBean接口的destroy()方法");
((DisposableBean) bean).destroy();
}
// 此用来将xml中配置的destroyMethodName不为空就进行反射调用,调用xml配置的销毁方法
// 如: destroy-method="destroyDataMethod" 的 destroyDataMethod就是要调用的方法,通过invoke()调用目标方法
if (StrUtil.isNotEmpty(destroyMethodName) && !(bean instanceof DisposableBean && "destroy".equals(this.destroyMethodName))) {
Method destroyMethod = bean.getClass().getMethod(destroyMethodName);
if (null == destroyMethod) {
throw new BeansException("Couldn't find a destroy method named '" + destroyMethodName + "' on bean with name '" + beanName + "'");
}
System.out.println("通过适配器-反射调用xml中配置的destroyDataMethod方法。");
destroyMethod.invoke(bean);
}
}
}
AbstractAutowireCapableBeanFactory:此类更改为registerDisposableBeanIfNecessary里判断
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefination) {
if (bean instanceof DisposableBean ||StrUtil.isNotEmpty(beanDefination.getDestroyMethodName())) {
registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefination));
}
}
DefaultSingletonBeanRegistry:修改要销毁方法的吊销容器,数据类型为DisposableBean, registerDisposableBean方法的第二个参数更改为DisposableBean,destroySingletons方法里更改为返回DisposableBean去接收处理。
// 要吊销的bean容器
private final Map<String, DisposableBean> disposableBeans = new HashMap<>();
public void registerDisposableBean(String beanName, DisposableBean bean) {
disposableBeans.put(beanName, bean);
}
public void destroySingletons() {
Set<String> keySet = this.disposableBeans.keySet();
Object[] disposableBeanNames = keySet.toArray();
System.out.println("从容器中取出要销毁的对象进行remove:" + disposableBeanNames.length);
for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
Object beanName = disposableBeanNames[i];
// 将容器中销毁的对象remove
DisposableBean disposableBean = disposableBeans.remove(beanName);
try {
// 调用用户自定义实现的销毁接口
disposableBean.destroy();
} catch (Exception e) {
e.printStackTrace();
throw new BeansException("Destroy method on bean with name '" + beanName + "' threw an exception", e);
}
}
}
userService的更改:通过实现初始化接口和销毁接口来实现自定义的方法可以自己去写相应的逻辑
public class UserService implements InitializingBean, DisposableBean {
private String uId;
private String company;
private UserDao userDao;
private String location;
public void queryUserInfo() {
System.out.println("查询用户信息:" + userDao.queryUserName(uId)
+",公司名称:"+company+",地址:"+location);
}
@Override
public void destroy() throws Exception {
System.out.println("执行接口实现方式的DisposableBean:UserService.destroy");
}
@Override
public void afterPropertiesSet() {
System.out.println("执行接口实现方式的InitializingBean:UserService.afterPropertiesSet");
}
}
万事具备,只欠测试了,和上一个一样的测试单元代码
@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);
userService.queryUserInfo();
}
执行:结果如下:
将xml配置的初始化和销毁方法都去掉就只执行接口处理的初始化和销毁方法。