Bean的初始化和销毁方法
代码地址:WangChao-ly/chao-spring at init-and-destroy-method (github.com)
建议订阅博主专栏,从下到上系统手写spring源码,体会其中过程!
在spring中,定义bean的初始化和销毁方法有三种方法:
- 在xml文件中制定init-method和destroy-method
- 继承自InitializingBean和DisposableBean
- 在方法上加注解PostConstruct和PreDestroy
第三种通过BeanPostProcessor实现,在扩展篇中实现,本节只实现前两种。
针对第一种在xml文件中指定初始化和销毁方法的方式,在BeanDefinition中增加属性initMethodName和destroyMethodName。
初始化方法在AbstractAutowireCapableBeanFactory#invokeInitMethods执行。DefaultSingletonBeanRegistry中增加属性disposableBeans保存拥有销毁方法的bean,拥有销毁方法的bean在AbstractAutowireCapableBeanFactory#registerDisposableBeanIfNecessary中注册到disposableBeans中。
为了确保销毁方法在虚拟机关闭之前执行,向虚拟机中注册一个钩子方法,查看AbstractApplicationContext#registerShutdownHook(非web应用需要手动调用该方法)。当然也可以手动调用ApplicationContext#close方法关闭容器。
到此为止,bean的生命周期如下:
初始化部分
- 通过xml加载初始化方法和销毁方法,在BeanDefinition中新增initMethodName和destroyMethodName,读取xml文件时候,获取这两个值,并且通过set方法注入到BeanDefinition中,这里代表着BeanDefinition处理完毕,下面是获取Bean时候处理的问题。
protected void doLoadBeanDefinitions(InputStream inputStream){
Document document = XmlUtil.readXML(inputStream);
Element root = document.getDocumentElement();
NodeList childNodes = root.getChildNodes();
for(int i=0;i<childNodes.getLength();i++){
if(childNodes.item(i) instanceof Element){
if(BEAN_ELEMENT.equals(((Element)childNodes.item(i)).getNodeName())){
Element bean = (Element)childNodes.item(i);
String id = bean.getAttribute(ID_ATTRIBUTE);
String name = bean.getAttribute(NAME_ATTRIBUTE);
String className = bean.getAttribute(CLASS_ATTRIBUTE);
String initMethodName = bean.getAttribute(INIT_METHOD_ATTRIBUTE);
String destroyMethodName = bean.getAttribute(DESTROY_METHOD_ATTRIBUTE);
Class<?> clazz = null;
try{
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new BeansException("Cannot find class [" + className + "]");
}
//id优于name
String beanName = StrUtil.isNotEmpty(id)?id:name;
if(StrUtil.isEmpty(beanName)){
//如果id和name都为空,将类名的第一个字母转为小写后作为bean的名称
beanName = StrUtil.lowerFirst(clazz.getSimpleName());
}
BeanDefinition beanDefinition = new BeanDefinition(clazz,new PropertyValues());
//设置初始和结束方法名
beanDefinition.setInitMethodName(initMethodName);
beanDefinition.setDestroyMethodName(destroyMethodName);
for(int j=0;j<bean.getChildNodes().getLength();j++){
if(bean.getChildNodes().item(j) instanceof Element){
if(PROPERTY_ELEMENT.equals(((Element)bean.getChildNodes().item(j)).getNodeName())){
//解析property标签
Element property = (Element) bean.getChildNodes().item(j);
String nameAttribute = property.getAttribute(NAME_ATTRIBUTE);
String valueAttribute = property.getAttribute(VALUE_ATTRIBUTE);
String refAttribute = property.getAttribute(REF_ATTRIBUTE);
if(StrUtil.isEmpty(nameAttribute)){
throw new BeansException("The name attribute cannot be null or empty");
}
Object value = valueAttribute;
if(StrUtil.isNotEmpty(refAttribute)){
value = new BeanReference(refAttribute);
}
PropertyValue propertyValue = new PropertyValue(nameAttribute, value);
beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
}
}
}
if(getRegistry().containsBeanDefinition(beanName)){
//beanName不能重名
throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");
}
//注册BeanDefinition
getRegistry().registerBeanDefinition(beanName,beanDefinition);
}
}
}
}
- 新建两个接口,一个是InitializingBean另一个是DisposableBean,实现了这两个接口的类,被标识为有初始化方法和有销毁方法的类,在我们进行createBean的时候会被用到。
- 在执行bean的初始化方法时,我们通过判断bean类型是不是InitializingBean来调用其定义的方法,然后通过在读取xml文件注入的initMethodName方法名,通过反射找到该方法,然后调用执行。
/**
* 执行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)){
//获取beanDefinition类中的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);
}
}
- 注册有销毁方法的bean,即bean继承自DisposableBean或有自定义的销毁方法,将这些元素注册到一个特殊的map中,到时候销毁时候,针对该map中的bean进行销毁操作。
protected void registerDisposableBeanIfNecessary(String beanName,Object bean,BeanDefinition beanDefinition){
if(bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())){
registerDisposableBean(beanName,new DisposableBeanAdapter(bean,beanName,beanDefinition));
}
}
其中DisposableBeanAdapter类是DisposableBean接口的具体实现类,用来注册到map中,然后提供具体的销毁方法。
private Map<String, Object> singletonObjects = new HashMap<>();
public void registerDisposableBean(String beanName,DisposableBean bean){
disposableBeans.put(beanName,bean);
}
销毁部分
- 在AbstractApplicationContext类中定义一个钩子,用来容器销毁时,自动释放销毁bean,然后提供close方法用来手动释放。
@Override
public void close(){
doClose();
}
protected void doClose(){
destroyBeans();
}
/**
* 调用容器中销毁bean的方法
*/
protected void destroyBeans(){
getBeanFactory().destroySingletons();
}
@Override
public void registerShutdownHook(){
Thread shutDownHook = new Thread(){
@Override
public void run() {
doClose();
}
};
Runtime.getRuntime().addShutdownHook(shutDownHook);
}
- ConfigurableListableBeanFactory接口继承了ConfigurableBeanFactory接口,我们在ConfigurableBeanFactory接口中定义void destroySingletons()方法,提供给上一步进行调用,具体的实现类在DefaultSingletonBeanRegistry中,该类负责单例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);
}
}
}
总结:
初始化:就是在创建时候,将初始化方法和销毁方法名set到BeanDefinition中,然后在执行bean的初始化方法,调用afterPropertiesSet和自定义的初始化方法,再将提供了销毁功能的类加入到Map中。
销毁:就是将加入到销毁的map中的元素全部移除,然后调用它的destroy方法。