欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring如何解决循环依赖问题

程序员文章站 2022-06-02 11:29:54
...

什么是循环依赖?

        比如A依赖B,而B又依赖A,当实例化A的时候,要实例化B,B又依赖A。然而A没有实例化完成,所以就陷入了死循环,

Spring是如何解决循环依赖的问题呢?

源码解读

在AbstractApplicationContext#refresh()方法中:
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
      prepareRefresh();
      // Tell the subclass to refresh the internal bean factory.
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      // Prepare the bean factory for use in this context.
      prepareBeanFactory(beanFactory);
      try {
         // Allows post-processing of the bean factory in context subclasses.
         postProcessBeanFactory(beanFactory);
         // Invoke factory processors registered as beans in the context.
         invokeBeanFactoryPostProcessors(beanFactory);
         // Register bean processors that intercept bean creation.
         registerBeanPostProcessors(beanFactory);
         // Initialize message source for this context.
         initMessageSource();
         // Initialize event multicaster for this context.
         initApplicationEventMulticaster();
         // Initialize other special beans in specific context subclasses.
         onRefresh();
         // Check for listener beans and register them.
         registerListeners();
         // Instantiate all remaining (non-lazy-init) singletons.
         //实例化剩余的非懒加载的单例bean
         finishBeanFactoryInitialization(beanFactory);
         // Last step: publish corresponding event.
         finishRefresh();
      }
      catch (BeansException ex) {
        ....................
      }
      finally {
        ''''''''''''''''''''
      }
   }
}

点进去finishBeanFactoryInitialization(beanFactory)->beanFactory.preInstantiateSingletons()->getBean(beanName)->AbstractBeanFactory#getBean(String name)->AbstractBeanFactory#doGetBean;

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
   final String beanName = transformedBeanName(name);
   Object bean;
   // Eagerly check singleton cache for manually registered singletons.
   Object sharedInstance = getSingleton(beanName);
    .....................
    ....................
}

重点看看getSingleton()这个方法

Spring如何解决循环依赖问题

Checks already instantiated singletons and also allows for an early reference to a currently created singleton (resolving a circular reference)

注释的意思是检查已经实例化的单例,并且允许提前引用正在创建的单例(解决循环引用)

/** Cache of singleton objects: bean name --> bean instance */
//一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
 
/** Cache of early singleton objects: bean name --> bean instance *///二级缓存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
 
/** Cache of singleton factories: bean name --> ObjectFactory */
//三级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

这里面有三级缓存,来学习下:

singletonObjects:用于保存beanName和实例化好的单例对象之间的关系,bean name -> bean instance;
earlySingletonObjects:也是保存beanName和创建实例之间的关系,存放提前曝光的单例对象,
                                        与前者的区别在于当一个单例放到里面后,那么当bean还在创建过程中,就可以通过getBean()获取,其目的是检测循环引用;
singletonFactories:用于保存beanName和创建bean的工厂之间的关系, 里面存放的是要被实例化的对象的对象工厂,bean name -> ObjectFactory.
 
理解三级缓存之后我们看getSingleton():
protected Object getSingleton(String beanName, boolean allowEarlyReference) {

    //从一级缓存获取单例
   Object singletonObject = this.singletonObjects.get(beanName);
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      //一级缓存没有并且当前单例bean正在创建
      synchronized (this.singletonObjects) {
         //从二级缓存获取
         singletonObject = this.earlySingletonObjects.get(beanName);
         if (singletonObject == null && allowEarlyReference) {
            //二级缓存没有且允许循环引用
            //从三级缓存获取对象工厂
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
               singletonObject = singletonFactory.getObject();
               //创建好放入到二级缓存
               this.earlySingletonObjects.put(beanName, singletonObject);
               //从三级缓存移除
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return singletonObject;
}

/**
* Return whether the specified singleton bean is currently in creation
* (within the entire factory).
* @param beanName the name of the bean
*/
public boolean isSingletonCurrentlyInCreation(String beanName) {
   return this.singletonsCurrentlyInCreation.contains(beanName);
}

想一下什么时候把对象工厂丢进三级缓存的?

下面继续:

前面AbstractBeanFactory#doGetBean()继续往下看,

// Create bean instance.
if (mbd.isSingleton()) {
   sharedInstance = getSingleton(beanName, () -> {
      try {
         return createBean(beanName, mbd, args);
      }
      catch (BeansException ex) {
         // Explicitly remove instance from singleton cache: It might have been put there
         // eagerly by the creation process, to allow for circular reference resolution.
         // Also remove any beans that received a temporary reference to the bean.
         destroySingleton(beanName);
         throw ex;
      }
   });
   bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

AbstractAutowireCapableBeanFactory#createBean() -> AbstractAutowireCapableBeanFactory#doCreateBean()

/**
* Actually create the specified bean. Pre-creation processing has already happened
* at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
* <p>Differentiates between default bean instantiation, use of a
* factory method, and autowiring a constructor.
*/
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {
   // Instantiate the bean.
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
   if (instanceWrapper == null) {
      //创建实例
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   ......
   ......
   ......
   // Eagerly cache singletons to be able to resolve circular references 提前缓存单例解决循环依赖问题
   // even when triggered by lifecycle interfaces like BeanFactoryAware.
   boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
         isSingletonCurrentlyInCreation(beanName));
   if (earlySingletonExposure) {
      if (logger.isDebugEnabled()) {
         logger.debug("Eagerly caching bean '" + beanName +
               "' to allow for resolving potential circular references");
      }
      //把对象工厂放入到三级缓存
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   }
   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
      //注入属性
      populateBean(beanName, mbd, instanceWrapper);
      //beanPostProcessorsBeforeInitialization()、initMethod()、beanPostProcessorsAfterInitialization()
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
   ......
      . . . . . . 
   return exposedObject;
}

/**
* Add the given singleton factory for building the specified singleton
* if necessary.
* <p>To be called for eager registration of singletons, e.g. to be able to
* resolve circular references.
*/
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
  
   synchronized (this.singletonObjects) {
      if (!this.singletonObjects.containsKey(beanName)) {
         //对象工厂放入三级缓存
         this.singletonFactories.put(beanName, singletonFactory);
         //二级缓存移除
         this.earlySingletonObjects.remove(beanName);
         this.registeredSingletons.add(beanName);
      }
   }
}

总结

所以当一个Bean调用构造函数进行实例化后,即使属性还未填充,就可以通过三级缓存向外暴露依赖的引用值(所以循环依赖问题的解决也是基于Java的引用传递),这也说明了另外一点,基于构造函数的注入,如果有循环依赖,Spring是不能够解决的。还要说明一点,Spring默认的Bean Scope是单例的,而三级缓存中都包含singleton,可见是对于单例Bean之间的循环依赖的解决,Spring是通过三级缓存来实现的。

其实在没有真正创建出来一个实例对象的时候,这个对象已经被生产出来了,虽然还不完美(还没有进行初始化的第二步和第三步),但是已经能被人认出来了(根据对象引用能定位到堆中

的对象),所以Spring此时将这个对象提前曝光出来让大家认识,让大家使用A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,发现

自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定

没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过

ObjectFactory.getObject拿到A对象(虽然A还没有初始化完全,但是总比没有好呀),B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects

中。此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,长大成人,进去了一级缓存singletonObjects中,而且更加幸运的是,由于B拿到了A的对象

总结:Spring通过三级缓存加上“提前曝光”机制,配合Java的对象引用原理,比较完美地解决了某些情况下的循环依赖问题!

相关标签: 源码 Spring