6、Mini-spring bean的初始化和销毁方法
前言:
前几章中,剩下Bean的初始化没有做,这一章,继续完善代码,实现bean的初始化与销毁方法。
实现:
在spring中,定义bean的初始化和销毁方法有三种方式:
- 在xml文件中定义init-method和destory-method
- 继承InitializingBean-afterPropertiesSet()方法 和DisposableBean
- 在方法上加注解@PostConstruct和@PreDestory
第三种通过BeanPostprocessor实现,在扩展篇中实现,本章只实现前两种。
xml文件配置方式
beanDefinition中新加对应的属性。xml中配置对应的方法。
/**
BeanDefinition 中新增属性
**/
private String initMethodName;
private String destroyMethodName;
/**
Person 对象中新增两个方法初始化和销毁
**/
public class Person {
private String name;
private int age;
private Car car;
public void customInitMethod() {
System.out.println("I was born in the method named customInitMethod");
}
public void customDestroyMethod() {
System.out.println("I died in the method named customDestroyMethod");
}
}
xml文件中配置初始化和销毁方法。
<bean id="person" class="org.springframework.test.ioc.bean.Person" init-method="customInitMethod" destroy-method="customDestroyMethod">
<property name="name" value="derek"/>
<property name="car" ref="car"/>
</bean>
继承接口方式:
定义InitializingBean:
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
定义DisposableBean:
public interface DisposableBean {
void destroy() throws Exception;
}
Person实体类继承这两个类,实现初始化和销毁的两个方法;
现在的person类是这样的。↓
public class Person implements InitializingBean, DisposableBean {
private String name;
private int age;
private Car car;
public void customInitMethod() {
System.out.println("I was born in the method named customInitMethod");
}
public void customDestroyMethod() {
System.out.println("I died in the method named customDestroyMethod");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("I was born in the method named afterPropertiesSet");
}
@Override
public void destroy() throws Exception {
System.out.println("I died in the method named destroy");
}
}
测试:
测试Test方法很简单。重点在new Application的时候,调用的getBean方法。
public class InitAndDestoryMethodTest {
@Test
public void testInitAndDestroyMethod() throws Exception {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:init-and-destroy-method.xml");
applicationContext.registerShutdownHook(); //或者手动关闭 applicationContext.close();
}
}
也就是最终会来到我们熟悉的doCreateBean()方法。这里和前面章节的区别显然就是一个initializeBean()初始化方法以及registerDisposableBeanIfNecessary()销毁方法啦。
protected Object doCreateBean(String beanName, BeanDefinition beanDefinition) {
Object bean = null;
try {
bean = createBeanInstance(beanDefinition);
//为bean填充属性
applyPropertyValues(beanName, bean, beanDefinition);
//执行bean的初始化方法和BeanPostProcessor的前置和后置处理方法
bean = initializeBean(beanName, bean, beanDefinition);
} catch (Exception e) {
throw new BeansException("Instantiation of bean failed", e);
}
//注册有销毁方法的bean
registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
addSingleton(beanName, bean);
return bean;
}
初始化
initializeBean()通过前几章,其实内部我们也很熟悉了。现在Bean已经完成实例化了,我们只要重点关注Bean初始化方法就好。
也就是最终会来到InvokeInitMethods,↓
protected void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Throwable {
//继承接口方式。
if (bean instanceof InitializingBean) {
((InitializingBean) bean).afterPropertiesSet();
}
//XML文件配置方法。
String initMethodName = beanDefinition.getInitMethodName();
if (StrUtil.isNotEmpty(initMethodName)) {
Method initMethod = ClassUtil.getPublicMethod(beanDefinition.getBeanClass(), initMethodName);
if (initMethod == null) {
throw new BeansException("Could not find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'");
}
initMethod.invoke(bean);
}
}
这个方法首先判断是不是InitializingBean的实例,而我们的Bean Person继承了InitializingBean,所以就会去执行Person内实现的afterPropertiesSet()进行初始化。这是实现接口继承的方式。
接着下面的代码就是xml文件的配置方式了,接口继承方式是通过Bean来判断的,而XML配置的方式,则是根据beanDefinition,获取初始化方法名称,获取到对应的方法名称后通过反射的方法实现初始化。
销毁:
接着看销毁Bean的实现。可以看到还是两种方式,一种通过bean判断是否是DisposableBean的实例,另一种是判断beanDefinition中是否有销毁的方法。我们在DefaultSingletonBeanRegistry中增加属性disposableBeans,用来保存拥有销毁方法的Bean。
注意这里第二个参数放入的是DisposableBeanAdapter()
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) {
registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
}
}
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
private Map<String, Object> singletonObjects = new HashMap<>();
private final Map<String, DisposableBean> disposableBeans = new HashMap<>();
public void registerDisposableBean(String beanName, DisposableBean bean) {
disposableBeans.put(beanName, bean);
}
}
为了确保销毁方法在虚拟机关闭之前执行,向虚拟机中注册一个钩子方法registerShutdownHook()(非web应用需要手动调用该方法)。也可以手动调用ApplicationContext#close方法关闭容器。这个钩子方法在虚拟机结束之前调用doClose()方法。
最终会调用DefaultSingletonBeanRegistry的destroySingletons()方法,将所有拥有销毁方法的Bean执行销毁方法。
public void destroySingletons() {
ArrayList<String> beanNames = new ArrayList<>(disposableBeans.keySet());
for (String beanName : beanNames) {
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中的DisposableBean是DisposableBeanAdapter,所以会调用它的destory方法。这个方法也兼容了两种方式,但是要保证销毁方法只执行一次。
public class DisposableBeanAdapter implements DisposableBean {
private final Object bean;
private final String beanName;
private final 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 {
if (bean instanceof DisposableBean) {
((DisposableBean) bean).destroy();
}
//避免同时继承自DisposableBean,且自定义方法与DisposableBean方法同名,销毁方法执行两次的情况
if (StrUtil.isNotEmpty(destroyMethodName) && !(bean instanceof DisposableBean && "destroy".equals(this.destroyMethodName))) {
//执行自定义方法
Method destroyMethod = ClassUtil.getPublicMethod(bean.getClass(), destroyMethodName);
if (destroyMethod == null) {
throw new BeansException("Couldn't find a destroy method named '" + destroyMethodName + "' on bean with name '" + beanName + "'");
}
destroyMethod.invoke(bean);
}
}
}
到现在为止,Bean的生命周期如下:
转载自:https://juejin.cn/post/7212958332931670077