当前位置:   article > 正文

spring的三级缓存与源码分析--解决循环依赖

spring的三级缓存与源码分析--解决循环依赖

三级缓存介绍

Spring 通过三层缓存来处理循环依赖,这些缓存分别是:

一级缓存(内存中的 singletonObjects)
二级缓存(earlySingletonObjects)
三级缓存(singletonFactories)

1. 一级缓存(singletonObjects)
这是 Spring 容器中的主要缓存区,存储已经完全初始化的单例 bean。当一个 bean 完全初始化并且所有依赖项已经注入完毕时,它会被放入这个缓存中。存储完全初始化好的单例 Bean。

2. 二级缓存(earlySingletonObjects)
这是一个存放“提前曝光”的 bean 的缓存区域。它存储的是那些已经开始实例化但还没有完成初始化的 bean。这个缓存用于解决那些在构造器注入过程中发生的循环依赖问题。存储原始的、未完全初始化的单例 Bean(提前暴露)。

3. 三级缓存(singletonFactories)
这是一个用于存储 ObjectFactory 的缓存区域,ObjectFactory 是一个工厂接口,用于创建 bean 实例。这个缓存用于在 bean 实例化过程中还未完成时提供一个工厂方法。存储原始的、带有 ObjectFactory 的单例 Bean。

循环依赖的解决流程

  • 创建 Bean 实例:当 Spring 容器创建一个 bean 实例时,它会首先检查一级缓存。如果在一级缓存中找不到该 bean,容器会继续执行实例化过程。
  • 二级缓存:在 bean 实例化的过程中,如果发现依赖的 bean 尚未完成初始化(即在创建过程中的“提前曝光”阶段),Spring 会将这个 bean 放入二级缓存中。
  • 三级缓存:如果需要依赖的 bean 还在创建过程中,Spring 会将一个 ObjectFactory 放入三级缓存中。这个 ObjectFactory 可以用来在稍后的阶段(即 bean 完全创建后)获取 bean 的实例。
  • 注入依赖:当 Spring 创建一个 bean 时,它会尝试从二级缓存中获取其依赖项。如果成功,它将使用这些“提前曝光”的 bean 完成注入。如果依赖项不在二级缓存中,Spring 会从三级缓存中使用 ObjectFactory 来获取最终的 bean 实例。
  • 完成初始化:一旦所有依赖项都被注入,bean 会被放入一级缓存中,并完成初始化过程。此时,它会被完全初始化,并准备好提供服务。

例如:

  1. public class A {
  2. @Autowired
  3. private B b;
  4. }
  5. @Component
  6. public class B {
  7. @Autowired
  8. private A a;
  9. }

在这种情况下,Spring 会使用三层缓存机制来处理这两个 bean 的循环依赖。具体流程如下:

1. 初始化 A 的过程
  1. 创建实例:Spring 发现需要创建 A 的实例。它首先会在一级缓存中查找 A,如果没有找到,再到二级缓存查找,最后到三级缓存查找。由于这是第一次创建 A,所以在所有缓存中都找不到。
  2. 实例化:Spring 创建 A 的原始实例(但不进行依赖注入)。此时,A 的原始实例会放到三级缓存中。
  3. 依赖注入:Spring 发现 A 依赖 B,于是开始创建 B
2. 初始化 B 的过程
  1. 创建实例:Spring 发现需要创建 B 的实例。它首先会在一级缓存中查找 B,如果没有找到,再到二级缓存查找,最后到三级缓存查找。由于这是第一次创建 B,所以在所有缓存中都找不到。
  2. 实例化:Spring 创建 B 的原始实例(但不进行依赖注入)。此时,B 的原始实例会放到三级缓存中。
  3. 依赖注入:Spring 发现 B 依赖 A。它会在一级缓存中查找 A,没有找到;再到二级缓存中查找,还是没有找到;最后到三级缓存中查找,找到了 A 的原始实例。
  4. 解决循环依赖:从三级缓存中获取到 A 的原始实例后,Spring 将 A 注入到 B 中。
  5. 完成 B 的初始化B 完全初始化后,Spring 将 B 的实例从三级缓存移到一级缓存。
3. 完成 A 的初始化
  1. 解决循环依赖:回到 A 的初始化过程,Spring 将完全初始化好的 B 注入到 A 中。
  2. 完成 A 的初始化A 完全初始化后,Spring 将 A 的实例从三级缓存移到一级缓存。

创建bean源码分析

AbstractAutowireCapableBeanFactory类的doCreateBean方法

  1. protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
  2.             throws BeanCreationException {
  3.         // Instantiate the bean.
  4.         // 实例化 Bean
  5.         BeanWrapper instanceWrapper = null;
  6.         if (mbd.isSingleton()) {
  7.             // 缓存了先前创建的 Bean 实例(如果有的话)。如果 Bean 是单例的,尝试从缓存中取出。
  8.             instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
  9.         }
  10.         if (instanceWrapper == null) {
  11.             // createBeanInstance 实际创建 Bean 实例的方法。通过反射调用构造函数或工厂方法来实例化 Bean。
  12.             instanceWrapper = createBeanInstance(beanName, mbd, args);
  13.         }
  14.         // instanceWrapper:包装了 Bean 实例和类型信息。getWrappedInstance() 获取实际的 Bean 实例,getWrappedClass() 获取 Bean 的类型
  15.         Object bean = instanceWrapper.getWrappedInstance();
  16.         Class<?> beanType = instanceWrapper.getWrappedClass();
  17.         if (beanType != NullBean.class) {
  18.             mbd.resolvedTargetType = beanType;
  19.         }
  20.         
  21.         // Allow post-processors to modify the merged bean definition.
  22.         // 应用合并的 Bean 定义后处理器
  23.         // mbd.postProcessingLock 确保在多线程环境中对 Bean 定义后处理的线程安全性。
  24.         synchronized (mbd.postProcessingLock) {
  25.             if (!mbd.postProcessed) {
  26.                 try {
  27.                     // 应用 Bean 定义后处理器。用于修改合并的 Bean 定义,比如处理 @PostConstruct 注解的方法
  28.                     applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
  29.                 }
  30.                 catch (Throwable ex) {
  31.                     throw new BeanCreationException(mbd.getResourceDescription(), beanName,
  32.                             "Post-processing of merged bean definition failed", ex);
  33.                 }
  34.                 mbd.postProcessed = true;
  35.             }
  36.         }
  37.         // Eagerly cache singletons to be able to resolve circular references
  38.         // even when triggered by lifecycle interfaces like BeanFactoryAware.
  39.         // 提前缓存单例以解决循环依赖
  40.         boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
  41.                 isSingletonCurrentlyInCreation(beanName));  // 如果 Bean 是单例的,并且允许循环依赖,且当前正在创建这个单例 Bean,则允许提前缓存
  42.         if (earlySingletonExposure) {
  43.             if (logger.isTraceEnabled()) {
  44.                 logger.trace("Eagerly caching bean '" + beanName +
  45.                         "' to allow for resolving potential circular references");
  46.             }
  47.             // addSingletonFactory:将一个 ObjectFactory 放入三级缓存,以便在 Bean 完全初始化后获取实例。这个 ObjectFactory 会在稍后阶段调用 getEarlyBeanReference 来获取 Bean 实例。
  48.             addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  49.         }
  50.         // Initialize the bean instance.
  51.         // 填充 Bean 属性和初始化 Bean
  52.         Object exposedObject = bean;
  53.         try {
  54.             // populateBean:填充 Bean 的属性
  55.             populateBean(beanName, mbd, instanceWrapper);
  56.             // initializeBean:初始化 Bean,比如执行 @PostConstruct 注解的方法,或者其他初始化逻辑
  57.             exposedObject = initializeBean(beanName, exposedObject, mbd);
  58.         }
  59.         catch (Throwable ex) {
  60.             // 处理可能发生的异常,并根据不同的情况抛出适当的异常
  61.             if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
  62.                 throw (BeanCreationException) ex;
  63.             }
  64.             else {
  65.                 throw new BeanCreationException(
  66.                         mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
  67.             }
  68.         }
  69.         
  70.         // 检查并处理循环依赖
  71.         if (earlySingletonExposure) {
  72.             // getSingleton(beanName, false):尝试从一级缓存中获取 Bean 实例。如果 Bean 尚未完全初始化,它会从三级缓存中获取到
  73.             Object earlySingletonReference = getSingleton(beanName, false);
  74.             if (earlySingletonReference != null) {
  75.                 if (exposedObject == bean) {
  76.                     exposedObject = earlySingletonReference;
  77.                 }
  78.                 // hasDependentBean(beanName):检查是否有其他 Bean 依赖于当前 Bean
  79.                 else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
  80.                     String[] dependentBeans = getDependentBeans(beanName);
  81.                     Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
  82.                     for (String dependentBean : dependentBeans) {
  83.                         // removeSingletonIfCreatedForTypeCheckOnly(dependentBean):移除仅用于类型检查的 Bean。
  84.                         if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
  85.                             actualDependentBeans.add(dependentBean);
  86.                         }
  87.                     }
  88.                     if (!actualDependentBeans.isEmpty()) {
  89.                         // BeanCurrentlyInCreationException:如果发现有依赖于当前 Bean 的其他 Bean,这个异常会被抛出,提示 Bean 已被提前注入而且存在循环依赖
  90.                         throw new BeanCurrentlyInCreationException(beanName,
  91.                                 "Bean with name '" + beanName + "' has been injected into other beans [" +
  92.                                 StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
  93.                                 "] in its raw version as part of a circular reference, but has eventually been " +
  94.                                 "wrapped. This means that said other beans do not use the final version of the " +
  95.                                 "bean. This is often the result of over-eager type matching - consider using " +
  96.                                 "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
  97.                     }
  98.                 }
  99.             }
  100.         }
  101.         // Register bean as disposable.
  102.         // 注册 Bean 为可销毁的
  103.         try {
  104.             // registerDisposableBeanIfNecessary:注册 Bean 的销毁回调方法(如果有的话),以便在容器销毁时调用
  105.             registerDisposableBeanIfNecessary(beanName, bean, mbd);
  106.         }
  107.         catch (BeanDefinitionValidationException ex) {
  108.             throw new BeanCreationException(
  109.                     mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
  110.         }
  111.         // 返回 Bean 实例
  112.         return exposedObject;
  113.     }

关键点

  1. // 提前缓存单例以解决循环依赖
  2. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
  3. isSingletonCurrentlyInCreation(beanName)); // 如果 Bean 是单例的,并且允许循环依赖,且当前正在创建这个单例 Bean,则允许提前缓存
  4. if (earlySingletonExposure) {
  5. if (logger.isTraceEnabled()) {
  6. logger.trace("Eagerly caching bean '" + beanName +
  7. "' to allow for resolving potential circular references");
  8. }
  9. // addSingletonFactory:将一个 ObjectFactory 放入三级缓存,以便在 Bean 完全初始化后获取实例。这个 ObjectFactory 会在稍后阶段调用 getEarlyBeanReference 来获取 Bean 实例。
  10. addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  11. }

一级缓存(内存中的 singletonObjects)
二级缓存(earlySingletonObjects)
三级缓存(singletonFactories)

  1. // 检查并处理循环依赖
  2. if (earlySingletonExposure) {
  3. // getSingleton(beanName, false):尝试从一二级缓存中获取 Bean 实例。如果 Bean 尚未完全初始化,它会从三级缓存中获取到
  4. Object earlySingletonReference = getSingleton(beanName, false);
  5. if (earlySingletonReference != null) {
  6. if (exposedObject == bean) {
  7. exposedObject = earlySingletonReference;
  8. }
  9. // hasDependentBean(beanName):检查是否有其他 Bean 依赖于当前 Bean
  10. else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
  11. String[] dependentBeans = getDependentBeans(beanName);
  12. Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
  13. for (String dependentBean : dependentBeans) {
  14. // removeSingletonIfCreatedForTypeCheckOnly(dependentBean):移除仅用于类型检查的 Bean。
  15. if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
  16. actualDependentBeans.add(dependentBean);
  17. }
  18. }
  19. if (!actualDependentBeans.isEmpty()) {
  20. // BeanCurrentlyInCreationException:如果发现有依赖于当前 Bean 的其他 Bean,这个异常会被抛出,提示 Bean 已被提前注入而且存在循环依赖
  21. throw new BeanCurrentlyInCreationException(beanName,
  22. "Bean with name '" + beanName + "' has been injected into other beans [" +
  23. StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
  24. "] in its raw version as part of a circular reference, but has eventually been " +
  25. "wrapped. This means that said other beans do not use the final version of the " +
  26. "bean. This is often the result of over-eager type matching - consider using " +
  27. "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
  28. }
  29. }
  30. }
  31. }

小结

  1. 实例化 Bean:创建 Bean 实例,使用缓存来优化性能和处理循环依赖。
  2. 处理循环依赖:使用三级缓存(singletonFactories、earlySingletonObjects 和 singletonObjects)来处理循环依赖。
  3. 初始化和属性填充:填充 Bean 属性并初始化。
  4. 循环依赖检查:确保在 Bean 初始化过程中处理循环依赖。
  5. 销毁回调:注册销毁方法,以便在 Bean 销毁时调用。

扩展--为什么有三层缓存还是会出现循环依赖问题

可能出现的原因:

  • 构造函数循环依赖

    • 三级缓存只能解决通过属性注入(setter injection)或字段注入(field injection)方式的循环依赖。
    • 如果两个或多个 Bean 之间存在构造函数循环依赖(即 A 的构造函数依赖于 B,B 的构造函数依赖于 A),那么即使有三级缓存机制,Spring 也无法解决这种循环依赖。
  1. @Component
  2. public class A {
  3. private B b;
  4. @Autowired
  5. public A(B b) {
  6. this.b = b;
  7. }
  8. }
  9. @Component
  10. public class B {
  11. private A a;
  12. @Autowired
  13. public B(A a) {
  14. this.a = a;
  15. }
  16. }

  • Bean 的作用域(Scope)

    • 三级缓存机制主要解决的是单例作用域(singleton scope)的循环依赖。
    • 对于多例作用域(prototype scope)的 Bean,Spring 不会缓存这些 Bean,因此无法解决原型作用域 Bean 的循环依赖。

spring bean的作用域:

Singleton(单例),Prototype(多例),Request(请求),Session(会话),Application(应用)

  • 自定义 Bean 后处理器(BeanPostProcessor)

    • 如果自定义的 BeanPostProcessor 中存在某些逻辑,导致提前访问 Bean 的实例,可能会打破三级缓存的逻辑,导致循环依赖异常。
    • 特别是 @Autowired 注解处理器或其他涉及提前引用 Bean 的处理器,可能导致 Bean 被提前实例化,从而引发循环依赖异常。
  • FactoryBean 的复杂依赖

    • FactoryBean 的复杂依赖关系可能导致 Spring 在创建 Bean 实例时遇到困难,从而引发循环依赖异常。
  • AOP 动态代理

    • AOP 动态代理的创建过程可能会打破三级缓存的逻辑。如果在代理创建过程中需要依赖某个 Bean,而这个 Bean 正在创建中,也可能导致循环依赖异常。

解决方法:

1. 使用 @Lazy 注解--懒加载

使用 @Lazy 注解可以推迟 Bean 的初始化,直到真正需要该 Bean 时才进行初始化。这样可以打破循环依赖链。

2. 使用 @PostConstruct 注解

使用 @PostConstruct 注解在 Bean 初始化之后执行某些操作,这样可以将依赖的初始化推迟到 Bean 完全创建之后。

3. 使用合适的注入方式

如:构造函数注入+ ObjectFactory; @Autowired 注解放在字段上; 使用 Setter 方法注入依赖

测试循环依赖

测试1:使用@Autowired srping自动处理

成功启动

测试2:使用构造函数注入--循环依赖出现

启动出现循环依赖

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/酷酷是懒虫/article/detail/919796
推荐阅读
相关标签
  

闽ICP备14008679号