内容介绍
在Spring框架中,你可以使用以下几种方法定义bean的初始化和销毁:
-
使用注解方式:
@PostConstruct
:在bean的方法上添加@PostConstruct
注解,该方法将在bean的依赖注入完成后立即执行,用于初始化操作。@PreDestroy
:在bean的方法上添加@PreDestroy
注解,该方法将在容器销毁该bean之前执行,用于清理资源或执行一些必要的关闭操作。
-
实现接口方式:
InitializingBean
接口:通过实现该接口,可以在bean的初始化阶段执行afterPropertiesSet()
方法,用于初始化操作。DisposableBean
接口:通过实现该接口,可以在bean销毁之前执行destroy()
方法,用于资源清理或关闭操作。
-
使用XML配置方式:
<bean>
元素的init-method
属性:在XML配置中,可以通过在<bean>
元素中使用init-method
属性来指定bean初始化时要调用的方法名。<bean>
元素的destroy-method
属性:类似地,可以使用destroy-method
属性指定bean销毁时要调用的方法名。
第一种方法将在BeanPostProcessor中实现,后面将编写文章专门介绍,本篇文章将介绍后两种实现方式。
针对第三种使用XML配置的方式,需要在BeanDefinition中增加属性initMethodName和destroyMethodName。初始化方法在AbstractAutowireCapableBeanFactory#invokeInitMethods执行。DefaultSingletonBeanRegistry中增加属性disposableBeans保存拥有销毁方法的bean,拥有销毁方法的bean在AbstractAutowireCapableBeanFactory#registerDisposableBeanIfNecessary中注册到disposableBeans中。
为了确保在虚拟机关闭之前执行销毁方法,使用AbstractApplicationContext类的registerShutdownHook()方法向虚拟机注册一个钩子方法。对于非web应用程序,需要手动调用该方法。另外,也可以通过手动调用ApplicationContext的close()方法来关闭容器。
当前bean的声明周期:
代码分支
核心代码
BeanDefinition
添加initMethodName和destroyMethodName属性,注意打印信息的打印顺序。
public class BeanDefinition {
private String initMethodName;
private String destroyMethodName;
//getter and setter ...
}
DisposableBean接口
销毁方法的定义,bean通过实现该接口的方式实现销毁前执行destory方法;
public interface DisposableBean {
void destroy() throws Exception;
}
DisposableBeanAdapter
设计模式(适配其模式)的适配类,适配xml形式的销毁方式,当类没有实现DisposableBean接口时候,可以定义Bean自己的销毁方法,在spring的xml中配置销毁方法使其注册到BeanDefinition中生效。
package org.springframework.beans.factory.support;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import java.lang.reflect.Method;
/**
* ● @author: YiHui
* ● @date: Created in 16:21 2023/6/25
*/
public class DisposableBeanAdapter implements DisposableBean {
private final Object bean;
private final String beanName;
private final String destroyMethodName;
public DisposableBeanAdapter(Object bean, String beanName, String destroyMethodName) {
this.bean = bean;
this.beanName = beanName;
this.destroyMethodName = destroyMethodName;
}
@Override public void destroy() throws Exception {
if(bean instanceof DisposableBean){
((DisposableBean)bean).destroy();
}
//DisposableBean的自定义的销毁方法命名为destroy时避免重复执行
if(StrUtil.isNotEmpty(destroyMethodName) && !((bean instanceof DisposableBean) && destroyMethodName.equals("destroy"))){
Method destroyMethod = ClassUtil.getPublicMethod(bean.getClass(), destroyMethodName);
if(destroyMethod == null){
throw new BeansException("Could not find destroy method [" + destroyMethodName + "] on bean [" + beanName + "]");
}
destroyMethod.invoke(bean);
}
}
}
DefaultSingletonBeanRegistry
添加disposableBean的注册方法和存储他的容器与移除销毁的方法
/**
* ● @author: YiHui
* ● @date: Created in 16:03 2023/2/20
* ● @notes: 默认单例bean注册类
*/
public abstract class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
private Map<String, Object> singletonObjects = new HashMap();
private Map<String, DisposableBean> disposableBeans = new HashMap();
@Override public Object getSingleton(String beanName) {
return singletonObjects.get(beanName);
}
@Override public void addSingleton(String beanName, Object singletonObject) {
singletonObjects.put(beanName, singletonObject);
}
protected abstract BeanDefinition getBeanDefinition(String beanName);
public void registerDisposableBean(String beanName, DisposableBean bean) {
disposableBeans.put(beanName, bean);
}
public void destroySingletons(){
List<String> beanNameList = new ArrayList<>(disposableBeans.keySet());
beanNameList.forEach(beanName ->{
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);
}
});
}
}
AbstractAutowireCapableBeanFactory
自动注册的beanDefinition工厂类添加方法,支持注册有销毁方法的bean(即继承自DisposableBean或有自定义的销毁方法并在xml定义的Bean)。当用户没有实现DisposableBean接口但是注册了自己的销毁方法在xml时,将利用此方法将beanDefinition中的方法名称提取,通过上面定义的DisposableBeanAdapter的方式在创建bean的时候存入单例beanFactory的容器中。
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements
AutowireCapableBeanFactory {
...
@Override protected Object createBean(String name, BeanDefinition beanDefinition,Object[] args) {
Object bean = null;
try {
bean = createBeanInstance(name,beanDefinition,args);
//填充属性
applyPropertyValues(name,bean, beanDefinition);
//执行bean的初始化方法和BeanPostProcessor的前置和后置处理方法
initializeBean(name, bean, beanDefinition);
}catch(Exception e){
throw new BeansException("Fail to instantiation",e);
}
//注册有销毁方法的bean
registerDisposableBeanIfNecessary(name, bean, beanDefinition);
addSingleton(name,bean);
return bean;
}
/**
* 支持注册有销毁方法的bean(即继承自DisposableBean或有自定义的销毁方法并在xml定义的Bean)
* @param beanName
* @param bean
* @param beanDefinition
*/
protected void registerDisposableBeanIfNecessary(String beanName,Object bean,BeanDefinition beanDefinition) {
if(bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) {
registerDisposableBean(beanName,new DisposableBeanAdapter(bean, beanName,beanDefinition.getDestroyMethodName()));
}
}
...
}
AbstractAutowireCapableBeanFactory
初始化方法的定义与调用
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements
AutowireCapableBeanFactory {
...
/**
* 执行bean的初始化方法
*
* @param beanName
* @param bean
* @param beanDefinition
* @throws Throwable
*/
protected void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Exception {
if(bean instanceof InitializingBean){
((InitializingBean)bean).afterPropertiesSet();
}
String initMethodName = beanDefinition.getInitMethodName();
if(StrUtil.isNotEmpty(initMethodName)){
Method initMethod = ClassUtil.getPublicMethod(bean.getClass(), initMethodName);
if(initMethod == null){
throw new BeansException("Could not find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'");
}
initMethod.invoke(bean);
}
}
...
}
XmlBeanDefinitionReader
最后不要忘记在reader的bean读取方法中将xml中bean的初始和销毁方法名称注入BeanDefinition。
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
...
public static final String INIT_METHOD_ATTRIBUTE = "init-method";
public static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method";
private void doLoadBeanDefinitions(InputStream inputStream){
//xml解析
String initMethodName = bean.getAttribute(INIT_METHOD_ATTRIBUTE);
String destroyMethodName = bean.getAttribute(DESTROY_METHOD_ATTRIBUTE);
...
beanDefinition.setInitMethodName(initMethodName);
beanDefinition.setDestroyMethodName(destroyMethodName);
...
}
...
}
测试
spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<bean id="person" class="bean.DisposablePerson" init-method="customInitMethod" destroy-method="customDestroyMethod">
<!-- <bean id="person" class="bean.DisposablePerson" init-method="customInitMethod" >-->
<property name="name" value="yiHui"/>
<property name="car" ref="car"/>
</bean>
<bean id="car" class="bean.Car">
<property name="name" value="Rolls-Royce"/>
</bean>
</beans>
测试方法
@Test
public void initAndDestroyMethodTest() {
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("classpath:spring.xml");
applicationContext.registerShutdownHook(); //或者手动关闭 applicationContext.close();
}
DisposablePerson
public class DisposablePerson extends Person implements DisposableBean, InitializingBean {
@Override public void destroy() throws Exception {
System.out.println("[ destroy ] :哦天呐!我要被干了");
}
@Override public void afterPropertiesSet() throws Exception {
System.out.println("[ afterPropertiesSet ] :哦天呐!我被发现了");
}
public void customInitMethod() {
System.out.println("customInitMethod : 我自己的初始化方法!");
}
public void customDestroyMethod() {
System.out.println("customDestroyMethod: 我自己的销毁方法!");
}
}
测试结果
[ afterPropertiesSet ] :哦天呐!我被发现了
customInitMethod : 我自己的初始化方法!
[ destroy ] :哦天呐!我要被干了
customDestroyMethod: 我自己的销毁方法!
Process finished with exit code 0