[源码系列:手写spring] IOC第十节:bean的初始化和销毁方法

本文介绍了在Spring框架中定义bean初始化和销毁的多种方式,包括注解@PostConstruct和@PreDestroy、实现InitializingBean和DisposableBean接口,以及使用XML配置的init-method和destroy-method属性。文章详细讲解了如何通过BeanDefinition、DisposableBeanAdapter和DefaultSingletonBeanRegistry等核心组件实现bean生命周期的管理,并提供了代码示例和测试用例。
摘要由CSDN通过智能技术生成

内容介绍

在Spring框架中,你可以使用以下几种方法定义bean的初始化和销毁:

  1. 使用注解方式:

    • @PostConstruct:在bean的方法上添加@PostConstruct注解,该方法将在bean的依赖注入完成后立即执行,用于初始化操作。
    • @PreDestroy:在bean的方法上添加@PreDestroy注解,该方法将在容器销毁该bean之前执行,用于清理资源或执行一些必要的关闭操作。
  2. 实现接口方式:

    • InitializingBean接口:通过实现该接口,可以在bean的初始化阶段执行afterPropertiesSet()方法,用于初始化操作。
    • DisposableBean接口:通过实现该接口,可以在bean销毁之前执行destroy()方法,用于资源清理或关闭操作。
  3. 使用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的声明周期:

当前bean的声明周期

代码分支

GitHub - yihuiaa/little-spring at init-and-destroy-method剖析Spring源码,包括常见特性IOC、AOP、三级缓存等... Contribute to yihuiaa/little-spring development by creating an account on GitHub.https://github.com/yihuiaa/little-spring/tree/init-and-destroy-method

核心代码

BeanDefinition

添加initMethodNamedestroyMethodName属性,注意打印信息的打印顺序。

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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值