Spring 如何解决循环依赖

核心成员变量

DefaultSingletonBeanRegistry:

/**
 * Cache of singleton objects: bean name to bean instance.
 * <p>单例对象的高速缓存:beam名称-bean实例,所有bean对象最终都会放到对象中</p>
 * */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/**
 * Cache of singleton factories: bean name to ObjectFactory.
 * <p>单例工厂的缓存:bean名称 - ObjectFactory </p>
 * */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/**
 * Cache of early singleton objects: bean name to bean instance.
 * <p>早期单例对象的高速缓存:bean名称 - bean实例</p>
 * <p>当从singletonFactories中获取到对应对象后,就会放到这个缓存中</p>
 * */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

用例

假设 A 依赖 B,B又依赖 A

步骤解释

  1. 将A的Bean名添加singletonsCurrentlyInCreation中表明起正在创建。使用A对应的 ObjectFactory 对象来创建A的对象.
    具体代码:
    DefaultSingletonBeanRegistry # getSingleton(String beanName, ObjectFactory<?> singletonFactory)
...
// 创建单例之前的回调,默认实现将单例注册为当前正在创建中
// 将beanName添加到 当前正在创建的bean名称列表【singletonsCurrentlyInCreation】后
beforeSingletonCreation(beanName);
try {
    
    
   //从单例工厂中获取对象,singletonFactory其实就是调 AbstractBeanFactory#createBean方法
// 创建出singletonObject
   singletonObject = singletonFactory.getObject();
   //生成了新的单例对象的标记为true,表示生成了新的单例对象
   newSingleton = true;
}
  1. 实例化 A 的对象
    具体代码:
    AbstractAutowireCapableBeanFactory # doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
...
//如果包装对象获取失败
if (instanceWrapper == null) {
    
    
   //使用适当的实例化策略为指定的BeanName创建一个新实例:工厂方法,
       // 构造函数自动装配或简单实例化。
   instanceWrapper = createBeanInstance(beanName, mbd, args);
}
/获取 instanceWrapperd包装的实例,此时还不算是Bean对象,只有最终加入了singletonObjects才算是Bean对象,这里是
// 只是算是beanName对应的实例对象
final Object bean = instanceWrapper.getWrappedInstance();
...
  1. 将A的对象再次封装到一个新的ObjectFactory对象,添加到 singletonFactories 中
    具体代码:
    AbstractAutowireCapableBeanFactory # doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
···
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// 饿汉式缓存 单例能够解决 循环依赖,即使被生命周期接口触发。
// 单例饿汉式暴露的情况标记 = 如果mdb的Bean对象是单例 且 允许自动解决循环依赖问题 且 该beanName正在创建(在整个工厂内)
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
      isSingletonCurrentlyInCreation(beanName));
//如果是单例饿汉式暴露的情况
if (earlySingletonExposure) {
    
    
   //打印跟踪信息
   if (logger.isTraceEnabled()) {
    
    
      logger.trace("Eagerly caching bean '" + beanName +
            "' to allow for resolving potential circular references");
   }
   //添加 新的单例对象工厂 singletonFactories  来构建 指定的单例对象。
   addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
···
  1. 注入 A 的依赖对象,如 B 的 Bean对象。
    AbstractAutowireCapableBeanFactory # doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
···
try {
    
    
   //【开始注入依赖对象】
   //用来自 BeanDefinition的属性值填充instanceWrapper中的bean实例
   populateBean(beanName, mbd, instanceWrapper);
   //初始化给定的Bean实例,应用工厂回调以及init方法和BeanPostProcessors,
   //        @Autowire的属性通过BeanPostProcessor注入
   exposedObject = initializeBean(beanName, exposedObject, mbd);
}
···
  1. 由于 B 还有被实例化,所以又会从 1~3 的步骤开始,得到 B 对应的 实例对象。到了第4步,需要注入 A对象,但由于 A 对象处于正在实例化的状态,但A的对象已经实例化出来,所以调用getBean方法获取A对象时,会走下面代码:
    AbstractBeanFactory # doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly)
...
// Eagerly check singleton cache for manually registered singletons.
// 译:认真检查单例缓存中是否有手动注册的单例。
// 获取beanName的单例对象,并允许创建早期引用。
//     其最终实现:DefaultSingletonBeanRegistry#getSingleton(beanName,true);
Object sharedInstance = getSingleton(beanName);
...

当sharedInstance不为null的时候,就会返回出去。所以直接来看 DefaultSingletonBeanRegistry # getSingleton(beanName) 具体实现

/**
 * 获取beanName的单例对象,并允许创建早期引用
 * @param beanName the name of the bean to look for - 要寻找的bean名
 * @see #getSingleton(String, boolean)
 */
@Override
@Nullable
public Object getSingleton(String beanName) {
    
    
   //获取beanName的单例对象,并允许创建早期引用
   return getSingleton(beanName, true);
}

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    
    
   //从单例对象的高速缓存中获取beanName的单例对象
   Object singletonObject = this.singletonObjects.get(beanName);
   //如果单例对象没有找到,并且 baneName 是正在被创建
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    
    
      //同步,以singletonObjects作为锁
      synchronized (this.singletonObjects) {
    
    
         //从早期单例对象的高速缓存中获取bean对象
         singletonObject = this.earlySingletonObjects.get(beanName);
         //如果获取不了bean的单例对象,且允许创建早期引用
         if (singletonObject == null && allowEarlyReference) {
    
    
            //从单例工厂的缓存中获取beanName的单例工厂对象
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            //如果beanName的单例工厂对象找到了
            if (singletonFactory != null) {
    
    
               //从beanName的单例工厂对象中获取该beanName的单例对象
               singletonObject = singletonFactory.getObject();
               //下面的操作主要是为了防止beanName对应的对象重复构建
               //添加beanName和其对应的beanName单例对象到 早期单例对象高速缓存中
               this.earlySingletonObjects.put(beanName, singletonObject);
               //从单例对象工厂缓存中移除beanName的单例对象工厂
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   //返回beanName对应的单例对象
   return singletonObject;
}

由于 A 对应的 ObjectFactory 对象已经将 添加到了 singletonFactories 中,调用 ObjectFactory#.getObject() 其实就是回调 第 3 步的 getEarlyBeanReference 方法:

/**
 * <p>该方法由 ObjectFactory实例的 getObject 方法 调用</p>
 * Obtain a reference for early access to the specified bean,
 * typically for the purpose of resolving a circular reference.
 * <p>获取对 指定Bean 的 早期访问引用,通常用于解决循环引用</p>
 * @param beanName the name of the bean (for error handling purposes) -- bean的名称(用于处理错误)
 * @param mbd the merged bean definition for the bean -- 合并后的BeanDefinition对象
 * @param bean the raw bean instance -- 原始Bean实例
 * @return the object to expose as bean reference -- 要公开为bean引用的对象
 */
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    
    
   //默认最终公开的对象是bean
   Object exposedObject = bean;
   //mbd的systheic属性:设置此bean定义是否是"synthetic",一般是指只有AOP相关的prointCut配置或者Advice配置才会将 synthetic设置为true
   //如果 mdb不是syntheic 且 此工厂拥有InstiationAwareBeanPostProcessor
   if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
    
    
      //遍历 工厂内的所有后处理器
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
    
    
         //如果 bp 是 SmartInstantiationAwareBeanPostProcessor实例
         if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
    
    
            SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
            //让exposedObject经过每个SmartInstantiationAwareBeanPostProcessor的包装
            exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
         }
      }
   }
   //返回最终经过层次包装后的对象
   return exposedObject;
}

然后将 getEarlyBeanReference 的返回结果作为A的实例对象添加到 earlySingletonObjects 中,并删除 singletonFactories 的记录。

  1. 此时 B 就能得到 A 的实例对象,然后注入到 B 的实例对象中,最后添加到 singletonObjects 中,并删除 singletonsCurrentlyInCreation 中B的记录,表明B的Bean对象已经构建完成。
    DefaultSingletonBeanRegistry # getSingleton(String beanName, ObjectFactory<?> singletonFactory)
...
    //创建单例后的回调,默认实现将单例标记为不在创建中
   afterSingletonCreation(beanName);
}
//生成了新的单例对象
if (newSingleton) {
    
    
   //将beanName和singletonObject的映射关系添加到该工厂的单例缓存中:
   addSingleton(beanName, singletonObject);
}
...
  1. 回到 A 的 构建步骤,此时由于B的Bean对象已经创建完成,A就可以正常注入B的Bean对象,最后
    添加到 singletonObjects 中,并删除 singletonsCurrentlyInCreation 中 A 的记录 以及 earlySingletonObjects 中A的记录,表明B的Bean对象已经构建完成。

可能会有的疑问

  1. earlySingletonObjects 的作用
    因为 Spring 需要保证每个单例对象只能实例化一次,singletonFactories 是用于实例对象的。如果有还有一种情况 B 还依赖于 C,C还依赖于 A 的情况,如果没有 earlySingletonObjects 会导致 A 的重复实例化。
  2. 为什么不将 singletonFactories 的实例化出来的对象直接放到 singletonObjects 中呢?
    因为 singletonFactories 实例化出来的对象还处于 创建中,还有很多的初始化操作未完成,而 singletonObjects 保存着的是最终的实例对象,是完全已经初始化好的对象,是最终的Bean对象。
  3. B 对象 获取到是 从 earlySingletonObjects 拿到 A 的对象,但是A此时还没有初始化完成,此时B 对象所得到的A对象会不会是不完整的呢?
    不会,因为A的对象即使还没有初始化完就已经注入B对象,但操作的对象都是同一个对象,所以B所注入的A对象最终再A初始化完以后也是一个完整的A对象。即使在注入时被 SmartInstantiationAwareBeanPostProcessor 封装成了另一个对象,但因为只要另一个对象有A对象的引用,所得到A对象也是完整的。

猜你喜欢

转载自blog.csdn.net/qq_30321211/article/details/109742743