Spring Bean生命周期详解

在对于Spring的所有解读中,Bean的生命周期都可谓是重中之重,甚至还有人称Spring就是个管理Bean的容器。Bean的生命周期之所以这么重要,被反复提及,是因为Spring的核心能力,比如对象创建(IOC)属性注入(DI)初始化方法的调用代理对象的生成(AOP)等功能的实现,都是在bean的生命周期中完成的。清楚了bean的生命周期,我们才能知道Spring的神奇魔法究竟是什么,是怎么一步步赋能,让原本普通的java对象,最终变成拥有超能力的bean的。

 bean的生命周期

Spring的生命周期大致分为:创建 -> 属性填充 -> 初始化bean -> 使用 -> 销毁 几个核心阶段。我们先来简单了解一下这些阶段所做的事情:

  • 创建阶段 主要是创建对象,这里我们看到,对象的创建权交由Spring管理了,不再是我们手动new了,这也是IOC的概念。

  • 属性填充阶段 主要是进行依赖的注入,将当前对象依赖的bean对象,从Spring容器中找出来,然后填充到对应的属性中去。

  • 初始化bean阶段 做的事情相对比较复杂,包括回调各种Aware接口、回调各种初始化方法、生成AOP代理对象也在该阶段进行,该阶段主要是完成初始化回调,后面我们慢慢分析。

  • 使用bean阶段 ,主要是bean创建完成,在程序运行期间,提供服务的阶段。

  • 销毁bean阶段 ,主要是容器关闭或停止服务,对bean进行销毁处理。

当然,bean的生命周期中还包括其他的流程,比如暴露工厂对象等,只是相对而言都是为其他功能做伏笔和准备的,再讲到对应功能时,我们在做详细分析。

实例化

BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
 instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
 instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
 mbd.resolvedTargetType = beanType;
}

这段代码的最终目的是为了获取到一个 bean 实例。获取之前先去检查如果有该 bean 尚未完成的 factoryBean 实例就先移除掉。

createBeanInstance 方法就是大家闭着眼睛也能猜出来的通过反射创建 bean 实例过程,最后我们拿到的 bean 实例就是这个 bean。

实例化完成之后,还有两个小细节。

一个是预留了后置处理器修改 BeanDefinition 的接口,在这里可以对 BeanDefinition 进行修改,这块通常用来处理通过注解注入值的情况,这个松哥在之前的文章中也有详细介绍过,小伙伴们参见:一个特殊的 BeanPostProcessor。

另外一个则是对于循环依赖的处理。

这里要做的工作就是根据当前 Bean 的情况,将 Bean 存入到三级缓存中(二级缓存中不存):

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
  isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

属性填充

populateBean(beanName, mbd, instanceWrapper);

这一句就是属性填充的环节了。属性填充就是一个 Bean 中我们通过各种注解如 @Autowired 等注入的对象,@Value 注入的字符串,这些统一都在 populateBean 中进行处理

初始化

exposedObject = initializeBean(beanName, exposedObject, mbd);

初始化主要是干这样四件事:

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;
}
  1. invokeAwareMethods:如果当前 bean 实现了 Aware 接口,那么 Aware 接口相关的方法就在 invokeAwareMethods 方法中被触发。

  2. applyBeanPostProcessorsBeforeInitialization:这个是执行 BeanPostProcessor#postProcessBeforeInitialization 方法。

  3. invokeInitMethods:这个里边是干两件事,如果我们的 Bean 实现了 InitializingBean 接口,那么该接口中的 afterPropertiesSet 方法就在这里被触发;另一方面就是如果我们通过配置文件 Bean 的初始化方法(XML 文件中的 init-method 属性),那么也会在这里被触发。

  4. applyBeanPostProcessorsAfterInitialization:这个是执行 BeanPostProcessor#postProcessAfterInitialization 方法。

这里需要注意的一点是,通过在 XML 文件中配置的 init-method 属性,这个是在第 3 步被触发执行的;但是如果是通过 @PostConstruct 注解标记的 Bean 的初始化方法,则是通过 BeanPostProcessor 来处理的,具体是在 InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization 方法中处理的。这两种看起来作用类似的 Bean 初始化方法,底层处理逻辑并不相同。

初始化完成之后,还有一个关于循环依赖的处理和判断。

if (earlySingletonExposure) {
 Object earlySingletonReference = getSingleton(beanName, false);
 if (earlySingletonReference != null) {
  if (exposedObject == bean) {
   exposedObject = earlySingletonReference;
  }
  else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
   String[] dependentBeans = getDependentBeans(beanName);
   Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
   for (String dependentBean : dependentBeans) {
    if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
     actualDependentBeans.add(dependentBean);
    }
   }
   if (!actualDependentBeans.isEmpty()) {
    throw new BeanCurrentlyInCreationException(beanName,
      "Bean with name '" + beanName + "' has been injected into other beans [" +
      StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
      "] in its raw version as part of a circular reference, but has eventually been " +
      "wrapped. This means that said other beans do not use the final version of the " +
      "bean. This is often the result of over-eager type matching - consider using " +
      "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
   }
  }
 }
}

 

这段代码主要是防止 Spring 容器中创建出来的当前 Bean 和被其他 Bean 所依赖的 Bean 不是同一个。例如有 A 和 B 两个类,Spring 根据既有配置,给 A 生成了代理类,但是 B 引用的并不是 A 的代理对象,而是 A 的原始对象,此时就会有问题。所以这里主要是去判断,确保容器中和被使用的 A 是同一个。

检查的思路就是先去二级缓存中查找,二级缓存中如果存在,说明这个 Bean 因为循环依赖的原因已经被引用过了(被引用过的 Bean 会存入到二级缓存中),此时去判断 exposedObject 和 bean 是否为同一个 Bean,正常情况下,这两个当然是同一个 Bean,因为 exposedObject 和 bean 指向同一个内存地址。什么情况下,这两个 Bean 会不同呢?如果在 Bean 的后置处理器中,我们使用新的 Bean 替换了旧的 Bean,就会导致最终拿到的 exposedObject 和 bean 两个变量指向的地址不再相同。如果不相同,就要检查当前 Bean 是否有被容器中的其他 Bean 所依赖了,如果有,并且使用了当前 Bean 的 Bean 还正在创建中,那么就赶紧删除掉重新创建,如果使用了当前 Bean 的 Bean 已经创建完成了,那就没办法了,只能抛出异常了。

销毁

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {
   // ...省略代码
   try {
      // 为bean注册DisposableBean,在容器关闭时,调用destory()
      registerDisposableBeanIfNecessary(beanName, bean, mbd);
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
   }

   return exposedObject;
}

在创建bean的时候,会判断如果bean是DisposableBeanAutoCloseable的子类,或者有 destroy-method等,会注册为可销毁的bean,在容器关闭时,调用对应的方法进行bean的销毁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值