likes
comments
collection
share

6、Mini-spring bean的初始化和销毁方法

作者站长头像
站长
· 阅读数 10

前言:

前几章中,剩下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初始化方法就好。

6、Mini-spring 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的生命周期如下:

6、Mini-spring bean的初始化和销毁方法