1. 程式人生 > 資訊 >軟銀在日本推出送餐機器人,幫助緩解用工荒:每月費用 6461 元

軟銀在日本推出送餐機器人,幫助緩解用工荒:每月費用 6461 元

概述

迴圈依賴就是依賴關係形成環,比如最簡單的迴圈依賴:A物件依賴B,B物件依賴A

屬性注入與迴圈依賴

  1. 如果是構造器注入,如果迴圈依賴物件沒法構建,因為還未例項化
  2. 如果是屬性注入但是作用域是prototype,spring不會快取其物件例項,也不能處理迴圈依賴的情況
  3. 如果是屬性注入singleton的,其bean的例項化過程與屬性注入過程是分開的,並且spring提供了三個map(就是大家說三級快取)來實現。

spring屬性注入處理迴圈依賴的方式

通過以下xml方式配置一個迴圈依賴的示例:

<bean id="person1" class="com.example.leetcode.spring.bean.Person">
  <property name="parent" ref="person2"></property>
  <property name="name" value="tom"></property>
</bean>

<bean id="person2" class="com.example.leetcode.spring.bean.Person">
  <property name="parent" ref="person1"></property>
  <property name="name" value="jack"></property>
</bean>

spring迴圈依賴處理幾個關鍵位置:

獲取bean物件

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;

  // 這裡會檢查單例bean是否已經在登錄檔,並返回。
  // Eagerly check singleton cache for manually registered singletons.
  Object sharedInstance = getSingleton(beanName);
  if (sharedInstance != null && args == null) {
    if (logger.isTraceEnabled()) {
      if (isSingletonCurrentlyInCreation(beanName)) {
        logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
            "' that is not fully initialized yet - a consequence of a circular reference");
      }
      else {
        logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
      }
    }
    bean = getObjectForBeanInstance(sharedInstance,name,beanName,null);
  }
  ...
}

DefaultSingletonBeanRegistry(單例物件登錄檔)的幾個關鍵屬性。

  // 用來儲存已經建立好的單例物件
  /** Cache of singleton objects: bean name to bean instance. */
  private final Map<String,Object> singletonObjects = new ConcurrentHashMap<>(256);

  // 用來儲存單例beanname到ObjectFactory的對映
  /** Cache of singleton factories: bean name to ObjectFactory. */
  private final Map<String,ObjectFactory<?>> singletonFactories = new HashMap<>(16);

  // 用來提前儲存還未初始化好的單例物件
  /** Cache of early singleton objects: bean name to bean instance. */
  private final Map<String,Object> earlySingletonObjects = new HashMap<>(16);

DefaultSingletonBeanRegistry.getSingleton()的實現.

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

AbstractAutowireCapableBeanFactory.doCreateBean建立物件與注入屬性

protected Object doCreateBean(final String beanName,final RootBeanDefinition mbd,final @Nullable Object[] args)
      throws BeanCreationException {
  ...
  instanceWrapper = createBeanInstance(beanName,mbd,args);
  ...
  // 檢查是否提前將單例bean存入快取
  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");
    }
    // 這裡將beanname與工廠對映放入快取登錄檔中(也就是上面的singletonFactories)
    addSingletonFactory(beanName,() -> getEarlyBeanReference(beanName,bean));
  }
  
  ...
  // 注入依賴屬性
  populateBean(beanName,instanceWrapper);
  ...
        
}

假設我們從beanfactory獲取person1物件, 迴圈依賴處理流程如下:

1.通過AbstractBeanFactory.doGetBean("persion1")獲取物件

2.因為一開始通過DefaultSingletonBeanRegistry.getSingleton()什麼都沒有,進入AbstractAutowireCapableBeanFactory.doCreateBean()進行建立

3.AutowireCapableBeanFactory.doCreateBean()裡面執行完建立邏輯,因為是singleton將beanname與工廠的對映加入到addSingletonFactory()到快取

4.開始處理person1物件的屬性依賴populateBean()

5.當發現person1的parent屬性是一個引用時,通過beanfactory.getBean("person2")獲取依賴物件(org.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveReference)

6.此時進入person2的建立流程, person2也沒有快取,開始例項化並加入到addSingletonFactory()到快取

7.person2在通過populateBean()注入屬性依賴發現依賴person1,此時通過beanfactory.getBean("person1")獲取依賴物件

8.此時AbstractBeanFactory.doGetBean("persion1")獲取物件執行到getSingleton("person1")進行以下判斷:

  • 從singletonObjects.get(beanName)獲取到null
  • 進入if條件,對singletonObjects同步
  • 從earlySingletonObjects.get(beanName);獲取也為null
  • 進入內層if,通過singletonFactories.get(beanName);獲取到最開始bean例項化之後的beanname與工廠快取資訊
  • 獲取到僅例項化完成的bean,並earlySingletonObjects.put(beanName,singletonObject);
  • 然後刪除singletonFactories.remove(beanName);

9.此時從getSingleton("person1")返回了一個僅例項化尚未注入的bean引用

10.person2在第7步獲取到person1僅例項化未注入的物件引用。

11.person2完成屬性注入並返回。

12.person2被addSingleton(beanName,singletonObject);中singletonObjects.put(beanName,singletonObject)快取,並刪除singletonFactories.remove(beanName);earlySingletonObjects.remove(beanName);

13.person1在5步獲取到person2的物件並完成屬性注入

14.person1物件返回(因為一開始person2獲取的是person1的引用,此時person1完成注入是能看到注入後的物件)

15.person1被addSingleton(beanName,singletonObject)快取,並刪除singletonFactories.remove(beanName);earlySingletonObjects.remove(beanName);

16.返回最終的person1物件

關於三個map(三級快取)

在出現迴圈依賴時,三個map之間的流程如下:

先從singletonFactories獲取工廠,並通過getObject獲取物件並移除快取,將物件快取到earlySingletonObjects
通過earlySingletonObjects獲取提前曝光的物件
物件建立並初始化完成之後,物件資訊保留在singletonObjects並移除過earlySingletonObjects中的快取

earlySingletonObjects二級快取是雞肋嗎?

earlySingletonObjects快取的目的是,通過三級快取在獲取物件會執行一些列的後置處理器,通過earlySingletonObjects來快取提升效能。

以上就是spring解決迴圈依賴的詳細內容,更多關於sping 迴圈依賴的資料請關注我們其它相關文章!