
常见的循环依赖
@Component
public class Aoo {
    @Autowired
    Boo boo;
}
@Component
public class Boo {
    @Autowired
    Aoo aoo;
}DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory<?>)最终调用AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[]) 实例化AaddSingleton将B加入一级缓存getSingleton("aoo", false)获取到二级缓存的对象, 调用addSingleton从二级缓存移除, A加入一级缓存BeanPostProcessor可能不会有这样的逻辑, 比如下文将提到的@Async), 如果已经被代理过, 则不再进行代理. 那Spring为什么不使用两级缓存而使用三级, 可能是一二级缓存合并后, 职责会混乱. 一级缓存存储完成品Bean, 二级缓存则是半成品.AnnotationAwareAspectJAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。如果出现了循环依赖,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理getSingleton方法将提前暴露的A对象转移到二级缓存后, 返回注入需要的A对象. A完成属性注入, 完成初始化, 再将A转移到一级缓存, 完成但即使有三级缓存也无法解决构造器的循环依赖, 对象无法正常实例化, 没有操作的空间
相关方法源码注释
DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
// 返回已注册在给定名称下的单例对象(原始)。
// 检查已实例化的单例对象,还允许早期引用当前创建的单例对象(以解决循环引用)
// @beanName 要查找的bean名称
// @allowEarlyReference 是否应创建早期引用
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 判断一级缓存
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 判断二级缓存
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 判断三级缓存
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 从三级缓存中拿到的工厂Bean获取到对象后, 从三级缓存中移除
                    singletonObject = singletonFactory.getObject();
                    // 加入二级缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}DefaultSingletonBeanRegistry#addSingleton
// 将给定的单例对象添加到当前工厂的单例缓存中 用于立即注册单例对象。
protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        // 加入一级缓存
        this.singletonObjects.put(beanName, singletonObject);
        // 从三级缓存移除
        this.singletonFactories.remove(beanName);
        // 从二级缓存移除
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
    }
}另外一个跟循环依赖相关的问题
总结一句话就是@Async的代理是由AsyncAnnotationBeanPostProcessor实现的, 其他代理比如@Transactional和@Aspect是AnnotationAwareAspectJAutoProxyCreator实现的, AnnotationAwareAspectJAutoProxyCreator调用父类AbstractAutoProxyCreator#postProcessAfterInitialization方法, 里面会判断当前bean是否是被早期暴露的, 如果是, 不进行处理. 但是AsyncAnnotationBeanPostProcessor会直接进行代理, 所以造成前后对象并不一致, 然后抛出异常
if (earlySingletonExposure) {
  // 获取到早期暴露出去的对象
  Object earlySingletonReference = getSingleton(beanName, false);
  // 早期暴露的对象不为null, 说明出现了循环依赖  
  if (earlySingletonReference != null) {
      // exposedObject是由上文initializeBean(beanName, exposedObject, mbd)返回的
      // 上文的initializeBean逻辑会调用实现了BeanPostProcessor的类对bean进行处理
      // 比如 @Async注解调用的就AbstractAdvisingBeanPostProcessor#postProcessAfterInitialization进行处理
      // 如果被代理了initializeBean方法返回的exposedObject 就会和 初始bean 不一样
      // 这个判断的意思就是指 postProcessAfterInitialization 回调没有进行动态代理
      // 如果没有那么就将早期暴露出去的对象赋值给最终生成出去的对象并返回
      // 这样就实现了早期暴露出去的对象和最终生成的对象是同一个了
      // 但是一旦 postProcessAfterInitialization 回调生成了动态代理
      // 那么就不会走这, 也就是加了@Aysnc注解,是不会走这的
      if (exposedObject == bean) {
          exposedObject = earlySingletonReference;
      }
      else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
               // allowRawInjectionDespiteWrapping 默认是false
               String[] dependentBeans = getDependentBeans(beanName);
               Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
               for (String dependentBean : dependentBeans) {
                   if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                       actualDependentBeans.add(dependentBean);
                  }
               }
               if (!actualDependentBeans.isEmpty()) {
                   // 抛出异常 放一下翻译的异常原文
                   // 创建名为'aoo'的bean时出错:
                   // 名为'aoo'的bean以其原始版本的形式作为循环引用的一部分注入到其他bean [boo]中
                   // 但最终已被包装。这意味着其他bean不使用bean的最终版本。
                   // 这通常是过于热衷于类型匹配的结果
                   // - 例如,考虑使用“getBeanNamesForType”并关闭“allowEagerInit”标志。
                   // 也就是说你这个bean被人给用了, 你现在又改变了这个bean
                   // 所以两边不一致了, 需要抛出异常终止
                   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 " +
                           "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
               }
      }
   }
}注意版本不一样实现逻辑可能有改变, 当前版本SpringBoot2.3.4
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。