spring 循環依賴的一次 理解

前言:

  在看spring 循環依賴的問題中,知道原理,網上一堆的資料有講原理。 但今天在看代碼過程當中,又產生了疑問。
疑問點以下:
// 疑問點: 先進行 dependon 判斷
String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } registerDependentBean(dep, beanName); try { getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } // 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); }
  想着這裏先會 判斷是否有依賴, bean 提早曝光的代碼在 檢查依賴的後面, 那循環依賴,不就進入死循環了嗎? 是否有通通疑問的朋友~~~ --也不知道當時這麼想的,只能說 還有提升的空間。
心想不對,寫了簡單的demo,開始debug了。
 
   結論:這裏的 mbd.getDependsOn() 只有在 配置了 depend-on 標籤的時候,纔會解析,有值。!!! 這也是致使 理解循環依賴 有問題的關鍵。

這裏先簡單記錄下 如今理解的 循環依賴的大體流程:spring

一、depende-on 標籤的狀況

<bean id="aService" class="com.zzf.spring.dependent.AService" depends-on="bService"/>
<bean id="bService" class="com.zzf.spring.dependent.BService" depends-on="aService"/>

注: depends-on適用於表面上看起來兩個bean之間沒有使用屬性之類的強鏈接的bean,可是兩個bean又確實存在先後依賴關係的狀況,使用了depends-on的時候,依賴他人的bean是先於被依賴bean銷燬的。 通常不會這麼使用。緩存

也就是這樣配置的狀況,纔會拋出 BeanCreationException 異常。app

if (isDependent(beanName, dep)) {// 判斷返回 true, 拋出循環依賴的exception
   throw new BeanCreationException(mbd.getResourceDescription(), beanName,
         "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}

  

二、正確的 xml 配置的循環依賴 demo

<bean id="aService" class="com.zzf.spring.dependent.AService">
    <property name="bService" ref="bService"/>
</bean>
<bean id="bService" class="com.zzf.spring.dependent.BService">
    <property name="aService" ref="aService"/>
</bean>

三、註解的方式解決循環依賴

@Service
public class Aservice {
    @Autowired
    private BService service;
}

@Service
public class BService {
    @Autowired
    private Aservice aservice;
}

  

都說這段是 解決 循環依賴的 關鍵所在:this

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) {
               singletonObject = singletonFactory.getObject();
               this.earlySingletonObjects.put(beanName, singletonObject);
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return singletonObject;
}

  這裏主要涉及到3個緩存, singletonObjects,earlySingletonObjects, singletonFactories。debug

  • singletonObjects: 單例對象的 cache
  • singletonFactories: 單例對象工廠的 cache
  • earlySingletonObjects: 提早曝光的單例對象的 cache。(這是關鍵)

 

這裏只考慮 A--B --A的狀況:xml

Object sharedInstance = getSingleton(beanName); 

  第一次 getBean(A)的時候, 返回是 null, 會走以下流程:對象

// 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);
}

  在 createBean -> doCreate中有以下: addSingletonFactory() 提早 曝光當前類 工廠,到 singletonFactory中blog

 

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");
   }
   addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

  而後執行 protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) 進行 屬性的 賦值。 在進行 B的屬性賦值中, 發現B尚未初始化, 則會去 調用 BeadFactory.getBean(B) 進行 B的初始化。ip

  調用B的過程當中,和A相似, 也會 提早 在singletonFactory 中曝光, 而後在 populateBean 中,注入 A屬性值時, 由於A未初始化,再次 去請求 getBean(A), 此次 在 getSingleton()中,由於 A提早曝光,因此在getSingleton 中 返回 A(可能未徹底初始化),最終調用 getObjectForBeanInstance 方法,返回 徹底實例話的 bean A, 而後注入到B 中,並完成B的 初始化, bean都會 放進singletonObjects 緩存中。ci

 

  TODO: 在 populateBean 中怎麼檢測 到 properties,這塊還需 仔細的去debug,還沒徹底理清楚。

 

參考資料:http://cmsblogs.com/?p=2887,看了屢次,總結的很好。

相關文章
相關標籤/搜索