從源碼上看Spring IOC如何解決bean之間循環依賴的問題


我們來探討一下Spring是如何解決循環依賴問題的。

什麼是循環依賴

先看一個示例圖吧:

在這裏插入圖片描述

好像很抽象的樣子,沒事,直接看代碼就很清晰了:

class A{
    private B b;
}
class B{
    private C c;
}
class C{
    private A a;
}

大概就是這麼一種依賴結構,對應的配置文件如下:

<bean id="beanA" class="com.jay.bean.A">
    <property name="b" ref="beanB"/>
</bean>
<bean id="beanB" class="com.jay.bean.B">
    <property name="c" ref="beanC"/>
</bean>
<bean id="beanC" class="com.jay.bean.C>
    <property name="a" ref="beanA"/>
</bean>

也就是這樣了。

我這樣模擬了一個:

public class StudentServiceImpl implements StudentService, BeanFactoryPostProcessor {

    int id;

    String name;

    Den den;

    public StudentServiceImpl() {
    }
}



//
public class Den {
    StudentService studentService;

    public StudentService getStudentService() {
        return studentService;
    }

    public Den() {
    }
}


public class StudentTest {
    public static void main(String[] args){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        StudentService studentService=(StudentService)applicationContext.getBean("studentService");
        studentService.study();
    }
}

我們都知道Spring IOC在創建完實例之後,需要對實例進行屬性填充,而此時如果發現了循環依賴的關係,會怎麼辦呢?

IOC使用是一種 允許未完成初始化的對象提前發佈 的一種思想來解決的,具體的實現呢,就是使用了三級緩存

三級緩存

/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

IOC中的三級緩存其實就是三個Map,並且這三個都是存放在DefaultListableBeanFactory這個工廠類對象中,DefaultListableBeanFactory應該不需要多解釋了。

  • singletonObjects:一級緩存是一個ConcurrentHashMap,存放着對象實例,我們調用getBean方法其實都是從這個map中直接進行獲取的
  • earlySingletonObjects:二級緩存是一個HashMap,也是存放着對象實例,但和一級緩存的區別就是,二級緩存存放的是尚未經過初始化的實例,也就是還未進行屬性賦值的實例
  • singletonFactories:三級緩存也是一個HashMap,存放着對應bean的工廠類對象

流程

上面的代碼寫了三個對象的相互依賴,這裏爲了簡便起見,我們只討論兩個對象的的依賴情況。

經過一系列流程,此時容器完成了初始化,配置文件也被解析成了beanDefinition對象,並且註冊到了beanFactory中,此時要做的,就是按照beanDefinition,開始進行bean的實例化了

對一個bean的實例化,從getBean方法開始,而getBean方法,會調用 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;

   // 調用getSingleton方法,這裏最終會去一級緩存中獲取,自然也只能獲取到空值了。
   Object sharedInstance = getSingleton(beanName);
   if (sharedInstance != null && args == null) {
      if (logger.isDebugEnabled()) {
         if (isSingletonCurrentlyInCreation(beanName)) {
            logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                  "' that is not fully initialized yet - a consequence of a circular reference");
         }
         else {
            logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
         }
      }
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }
   //獲取到空值,那麼就要進行實例化了
   else {
      ...

      try {
         ...

         // 如果是單例
         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);
         }

         else if (mbd.isPrototype()) {
            // It's a prototype -> create a new instance.
            Object prototypeInstance = null;
            try {
               beforePrototypeCreation(beanName);
               prototypeInstance = createBean(beanName, mbd, args);
            }
            finally {
               afterPrototypeCreation(beanName);
            }
            bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
         }

         ...
      }
      catch (BeansException ex) {
         cleanupAfterBeanCreationFailure(beanName);
         throw ex;
      }
   }

   ...
   return (T) bean;
}

那就看一下createBean()吧:

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {

   if (logger.isDebugEnabled()) {
      logger.debug("Creating instance of bean '" + beanName + "'");
   }
    //獲取該bean的模板,也就是beanDefinition實例
   RootBeanDefinition mbdToUse = mbd;

   // Make sure bean class is actually resolved at this point, and
   // clone the bean definition in case of a dynamically resolved Class
   // which cannot be stored in the shared merged bean definition.
   Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
   if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
      mbdToUse = new RootBeanDefinition(mbd);
      mbdToUse.setBeanClass(resolvedClass);
   }

   // Prepare method overrides.
   try {
      mbdToUse.prepareMethodOverrides();
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
            beanName, "Validation of method overrides failed", ex);
   }

   try {
      // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
      Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
      if (bean != null) {
         return bean;
      }
   }
   catch (Throwable ex) {
      throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
            "BeanPostProcessor before instantiation of bean failed", ex);
   }

   try {
       //重點是這裏
      Object beanInstance = doCreateBean(beanName, mbdToUse, args);
      if (logger.isDebugEnabled()) {
         logger.debug("Finished creating instance of bean '" + beanName + "'");
      }
      return beanInstance;
   }
   catch (BeanCreationException ex) {
      // A previously detected exception with proper bean creation context already...
      throw ex;
   }
   catch (ImplicitlyAppearedSingletonException ex) {
      // An IllegalStateException to be communicated up to DefaultSingletonBeanRegistry...
      throw ex;
   }
   catch (Throwable ex) {
      throw new BeanCreationException(
            mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
   }
}

看看doGetBean裏:

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) {
       //這一步纔是真正地,將bean對象實例化了,並且存放在instanceWrapper這個包裝類對象中
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
    //從包裝類對象中獲取到bean實例
   final Object bean = instanceWrapper.getWrappedInstance();    //1
   Class<?> beanType = instanceWrapper.getWrappedClass();
   if (beanType != NullBean.class) {
      mbd.resolvedTargetType = beanType;
   }

   // Allow post-processors to modify the merged bean definition.
   synchronized (mbd.postProcessingLock) {
      if (!mbd.postProcessed) {
         try {
            applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
         }
         catch (Throwable ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                  "Post-processing of merged bean definition failed", ex);
         }
         mbd.postProcessed = true;
      }
   }

   // 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);
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
   catch (Throwable ex) {
      if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
         throw (BeanCreationException) ex;
      }
      else {
         throw new BeanCreationException(
               mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
      }
   }

   ...

   ...

   return exposedObject;
}

現在bean實例已經完成了,按理來說我們可以直接返回了,但事實上,我們還需要寫緩存呢,因爲要解決依賴問題,所以接下來就看看addSingletonFactory方法裏面做了什麼:

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(singletonFactory, "Singleton factory must not be null");
   synchronized (this.singletonObjects) {
      if (!this.singletonObjects.containsKey(beanName)) {
         this.singletonFactories.put(beanName, singletonFactory);
         this.earlySingletonObjects.remove(beanName);
         this.registeredSingletons.add(beanName);
      }
   }
}

做了這麼幾件事:

1.寫三級緩存。(看看三級緩存裏面都有什麼):

在這裏插入圖片描述

看singletonFactories屬性中,已經存入了一個工廠類對象,裏面存儲着一個StudentServiceImpl@2067,正是我們剛剛實例化完成,但還沒有進行賦值的那個對象

2.從二級緩存中移除(當然二級緩存中目前什麼也沒有)

接下來就是要進行屬性注入了,其中也要注入den屬性,這裏就發生循環依賴了,populateBean方法就不仔細看了,裏面就是會循環bean實例裏面的參數進行賦值,當循環到den屬性時,發現是一個對象,那麼就要進行den的初始化了。在一級二級三級緩存都沒有發現den對象,那麼就要對den進行初始化了,那den的初始化和StudentServiceImpl初始化類似,都是調用:

getBean — > doGetBean —> getSingleton這樣子 ,獲取到den的實例(還沒有初始化噢!),同樣也要註冊到三級緩存中。同樣,也要進行賦值,在賦值時,因爲den也依賴了studentServiceImpl,所以它同樣要給studentServiceImpl賦值,同樣要去一級二級三級緩存中查找,而這次不同的是,它會在三級緩存中找到studentServiceImpl對象對應的工廠類對象,於是就能獲取到了studentServiceImpl對象,而這個studentServiceImpl對象正是我們剛剛創建的那個,獲取到它之後,賦值給den,那麼den的實例化就算是完成了,雖然den鎖依賴的studentServiceImpl對象還沒有完成初始化,也就是它內部的屬性都是一個原始值,但是沒關係,den對象完成了實例化就足夠了
去一級二級三級緩存查找的過程如下:

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) {
                //singletonFactories已經有一個值了,在創建studentServiceImpl實例(未初始化)時就已經經過一次
                //addSingletonFactory操作了,將“早期引用”存放在這裏了,所以這裏直接獲取到了
                //取出該“早期引用”,裏面是一個studentServiceImpl實例,但屬性都是原始值
               singletonObject = singletonFactory.getObject();
               this.earlySingletonObjects.put(beanName, singletonObject);
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
    //返回
   return singletonObject;
}

den初始化完成了,然後就是回到studentServiceImpl的賦值階段了唄,den已經有了,直接賦值,ok,至此,studentServiceImpl對象的創建就已經完成了,後續就是將該對象存到一級緩存中,使得後續調用getBean可以直接在一級緩存中獲取該對象。ok,那麼這樣的話,對象的創建就大功告成啦!循環依賴的問題也解決了。

最後再以一道面試題來總結下博客的內容吧~

面試官:IOC是如何解決循環依賴的問題的呢?

A:IOC解決循環依賴的思想就是:將初始化還未完成的對象提前發佈。

對應的實現呢,就是ioc中的beanFactory維護了三個級別的緩存

  • 一級緩存叫做singletonObjects,它是一個ConcurrentHashMap,它存的value就是bean對象實例了,而後續調用getBean方法其實也是從這個map裏獲取
  • 二級緩存叫做earlySingletonObjects,它是一個HashMap,而它存的也是bean對象實例,但和一級緩存的區別就是,二級緩存存的實例還未進行初始化,也就是所有屬性值都只是數據類型的初始值。
  • 三級緩存叫做singletonFactories,它也是一個HashMap,存的是bean的工廠類。

假設有兩個類是A,B是互相依賴的,那麼首先要對A類進行實例化。實例化的入口就是getBean這個方法,對應的操作就是以該bean的beanDefinition對象作爲模板,進行實例化,實例化之後,將該對象包裝成工廠類,註冊到三級緩存之中,而這一步就是實現循環依賴的一個關鍵所在了。進行了實例化之後,再對對象進行初始化,也就是屬性賦值,當賦值到B對象,卻發現一級二級三級緩存中都沒有B對象的值時,此時就要去創建B對象,同樣需要實例化,然後初始化的過程,那B對象的初始化過程也需要給A賦值,然後就去查找,最終在三級緩存中獲取到了對象A,然後賦值給對象B,並且將三級緩存的數據添加到二級緩存後進行清楚。至此B對象就已經實例化完畢了,雖然B對象中的A對象只是進行實例化,還未進行初始化,但B對象能夠成功實例化,就已經足夠了。B對象實例化完之後,就可以賦值給A對象了,然後A對象完成初始化,註冊在一級緩存中,那麼整個過程就完成了。也解決了互相依賴的問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章