Loading

Spring 中 Bean 的生命周期

在Spring框架中,"bean"这一术语特指那些受Inversion of Control (IoC) 容器管理的对象,它们通常都通过Dependency Injection (DI) 自动装配,无需开发者直接干预其生命周期管理。然而,在某些场景下,对特定 bean 进行定制化的初始化与销毁操作成为必要,此时就可以通过 Spring 中的拓展接口来实现。

基本生命周期

Spring 中 IoC 的顶层接口是 BeanFactory,默认实现是 DefaultListableBeanFactory,我们可以在 BeanFactory 的注释文档中看到关于 bean 生命周期的介绍,其主要有以下几类:

初始化

以下初始化方法都发生在属性注入之后

  1. 调用各种实现了 Spring 中自带的 Aware 接口中的方法

  2. 执行实现了 BeanPostProcessor 接口的 postProcessBeforeInitialization 方法

  3. 执行实现了 InitializingBean 接口的 afterPropertiesSet 方法

  4. 调用 bean 的 init-method

  5. 执行实现了 BeanPostProcessor 接口的 postProcessAfterInitialization 方法

销毁

  1. 执行实现了 DestructionAwareBeanPostProcessor 接口的 postProcessBeforeDestruction 方法

  2. 执行实现了 DisposableBean 接口的 destroy 方法

  3. 调用 bean 的 destroy-method

bean 整体的生命周期阶段可以分为:实例化、属性填充、初始化、running 和销毁,相关的拓展主要在初始化和销毁阶段

实现原理

Spring 会在 doCreateBean 过程中的 populateBean 方法之后执行 initializeBean 方法,该方法会按照 BeanFactory 文档中描述的顺序依次执行对应的生命周期钩子函数

  protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
      invokeAwareMethods(beanName, bean);

      Object wrappedBean = bean;
      if (mbd == null || !mbd.isSynthetic()) {
          wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
      }

      try {
          invokeInitMethods(beanName, wrappedBean, mbd);
      }
      catch (Throwable ex) {
          throw new BeanCreationException(
                  (mbd != null ? mbd.getResourceDescription() : null), beanName, ex.getMessage(), ex);
      }
      if (mbd == null || !mbd.isSynthetic()) {
          wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
      }

      return wrappedBean;
  }

拓展生命周期

在 Spring 中,除了 BeanFactory 中描述的钩子之外,还通过 CommonAnnotationBeanPostProcessor 提供了对 JSR-250 的 @PostConstruct and @PreDestroy 的支持 (是基于 BeanPostProcessor 接口实现的)

顺序总览

因此,在 Spring 中的 createBean 阶段共提供了 4 对针对 bean 初始化和销毁的生命周期函数钩子,他们的执行顺序依次为:

  1. BeanPostProcessor#postProcessBeforeInitialization

  2. @PostConstruct

  3. InitializingBean#afterPropertiesSet

  4. init-method

  5. BeanPostProcessor#postProcessBeforeDestruction

  6. @PreDestroy

  7. DisposableBean#destroy

  8. destroy-method

区别

  • BeanPostProcessor 可以实现对已经实例化的 bean 进行替换,而其他钩子函数无法修改 bean 的引用对象

  • @PostConstruct、@PreDestroy 组合的钩子函数可以实现应用与 Spring 的解耦

  • init-method、destroy-method 相较于 InitializingBean、DisposableBean 的组合可以实现业务代码与 Spring 的解耦,但是不常用

应用级生命周期

以上所讲的生命周期钩子函数都是围绕单个 bean 的 createBean 阶段进行的,这个阶段通常在容器的单例创建锁中执行的。此阶段中的 bean 可能还未完全初始化,不应该在此阶段中调用外部 bean,因为存在初始化死锁或循环引用的风险。

@Configuration(proxyBeanMethods = false)
class CircularReference {
    @Component
    class C1(): BeanFactoryAware {
        override fun setBeanFactory(beanFactory: BeanFactory) {
            println(beanFactory.getBean(C1::class.java))
        }
    }

    @Component
    class C2(): BeanFactoryAware {
        override fun setBeanFactory(beanFactory: BeanFactory) {
            println(beanFactory.getBean(C1::class.java))
        }
    }
}

针对以上问题,可以通过应用级的生命周期钩子函数来解决,通常有以下几种方案:

  • 实现 SmartInitializingSingleton 接口

  • 实现 SmartLifecycle 接口

  • 监听 ContextRefreshedEvent 事件

调用时机

  1. SmartInitializingSingleton 对应的钩子函数会在 Spring 应用 refresh 阶段中所有单例 bean 初始化完成后触发,适合执行那些依赖整个容器状态的操作。

  2. SmartLifecycle 对应的钩子函数会在 Spring 应用 refresh 阶段中的 finishRefresh 方法中调用。

  3. ContextRefreshedEvent 事件也会在 Spring 应用 refresh 阶段中的 finishRefresh 方法中发布。

Lifecycle 和 SmartLifecycle 的区别是:后者的钩子函数会在 Spring 应用的 refresh 阶段被自动调用,而前者需要手动调用 application 的 start 方法

总结

Spring框架通过高度可定制的bean生命周期管理模型,赋予了开发者强大的控制能力,不仅能够精确地控制单个bean的行为,还能有效地管理整个应用的启动与关闭流程,是构建高质量、可维护的Java应用不可或缺的一部分。

posted @ 2024-05-03 18:00  xtyuns  阅读(11)  评论(0编辑  收藏  举报