1. 程式人生 > 程式設計 >SpringIOC DI迴圈依賴例項詳解

SpringIOC DI迴圈依賴例項詳解

要弄清楚迴圈依賴

1、需要知道Bean初始化的兩個階段

① Bean例項化建立例項物件(new Bean())

② Bean例項物件初始化(DI:註解自動注入)

2、DefaultSingletonBeanRegistry類中的5個容器

/** 記錄已將建立的單例<beanName,singletonBean> */
  private final Map<String,Object> singletonObjects = new ConcurrentHashMap<>(256);

  /** 記錄singletonFactory<beanName,singletonFactory> singeletonFactory中存放beanName和上面的①階段的bean:Bean例項化例項物件(還未初始化DI)*/
  private final Map<String,ObjectFactory<?>> singletonFactories = new HashMap<>(16);

  /** 記錄早期的singletonBean 存放的也是① */
  private final Map<String,Object> earlySingletonObjects = new HashMap<>(16);

  /** 存放已經初始化後的beanName,有序 */
  private final Set<String> registeredSingletons = new LinkedHashSet<>(256);

  /** 記錄正在初始化的bean的beanName */
  private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

其中跟迴圈依賴相關的是singletonFactories、singeletonsCurrentlyInCreation、earlysingletonObjects.

3、迴圈依賴實現

①bean初始化前後會打標,加入到singletonsCurrentlyInCreation容器中,這個打標會在核心方法getSingleton()中起作用

/* org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String,org.springframework.beans.factory.ObjectFactory<?>) */
  public Object getSingleton(String beanName,ObjectFactory<?> singletonFactory) {
    ...
    //迴圈依賴相關:初始化前先singletonsCurrentlyInCreation.add(beanName)
    beforeSingletonCreation(beanName);
    ...
    //lamda表示式:其實是呼叫createBean(beanName,mbd,args):Bean初始化
    singletonObject = singletonFactory.getObject();
    ...
    //迴圈依賴相關:初始化後singletonsCurrentlyInCreation.remove(beanName)
    afterSingletonCreation(beanName);
    ...//初始化完後
    //this.singletonObjects.put(beanName,singletonObject);放入到單例容器中
    //this.singletonFactories.remove(beanName);清空迴圈依賴的兩個打標
    //this.earlySingletonObjects.remove(beanName);
    //this.registeredSingletons.add(beanName);放入單例beanName容器中
    addSingleton(beanName,singletonObject);
    ...
    }
  }

② 上面singletonObject = singletonFactory.getObject()時向singletonFactories中記錄了(new Bean()),singletonFactories也會在核心方法getSingleton()中起作用

/* org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean */
  protected Object doCreateBean(final String beanName,final RootBeanDefinition mbd,final @Nullable Object[] args)
      throws BeanCreationException {
    ...
    //迴圈依賴相關邏輯:
    //this.singletonFactories.put(beanName,singletonFactory);
    //將例項化bean(①階段)、beanName組裝成singletonFactory裝入singletonFactories容器
    //this.earlySingletonObjects.remove(beanName);
    //刪除earlySingletonObjects中beanName
    addSingletonFactory(beanName,() -> getEarlyBeanReference(beanName,bean));
    ...
    //例項初始化 就是在這裡面實現依賴注入DI的:反射實現
    //呼叫AutowiredAnnotationBeanPostProcessor.postProcessProperties
    populateBean(beanName,instanceWrapper);
    ...
  }

③ 核心方法getSingleton

/* org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String,boolean) */
  protected Object getSingleton(String beanName,boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
     //迴圈依賴核心就在於這個判斷,由於打標+記錄了①階段的bean,
    //迴圈依賴第二次呼叫getBean("a")時,這裡會直接返回第一次呼叫getBean("a")建立的①階段的bean
    //而不會呼叫createBean("a")再次bean初始化(造成兩個bean的迴圈建立)
    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;
  }

④ 迴圈依賴流程

/* org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean */
  protected <T> T doGetBean(final String name,@Nullable final Class<T> requiredType,@Nullable final Object[] args,boolean typeCheckOnly) throws BeansException {
    ...
    //假設A、B互相依賴
    //第一次getBean(A),sharedInstance == null,走else,createBean
    //A正在建立打標,①中beforeSingletonCreation()
    //A例項化後儲存到singletonFactories中②中addSingletonFactory(beanName,singletonFactory)
    //DI依賴注入:②中populateBean(beanName,instanceWrapper),發現依賴B,呼叫getBean(B)初始化B的單例
    //呼叫getBean(B)重複上面步驟,DI依賴注入發現依賴A,呼叫getBean(A)
    //第二次getBean(A),③中if(singletonObject == null && isSingletonCurrentlyInCreation(A))由於打標了所以返回singleFactory.getObject()
    //下面if條件直接返回bean,沒有走else破壞了迴圈
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
      //
      bean = getObjectForBeanInstance(sharedInstance,name,beanName,null);
    }

    else {
      ...
      //
      createBean(beanName,args);
      bean = getObjectForBeanInstance(sharedInstance,null);
    }
    return bean;      
  }

四、總結

未看原始碼之前,其實對迴圈依賴有一個想法:迴圈依賴可以看做是一個死鎖。

預防死鎖的方法:打破死鎖的四個必要條件(互斥、請求並等待、不可剝奪、迴圈等待),由於迴圈依賴的資源是物件自身,所以常用破壞迴圈等待條件方法:編號順序執行,不適用

選擇破壞請求並等待條件:先建立物件,再賦值,模型

A a = new A();
B b = new B();
A.b = b;
B.a = a;

研究原始碼之後發現:想法差不多,但是程式碼實現非常精彩。模型(打標沒想到過)

A a = new A();
B b = new B();
b.a = a;
a.b = b;

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。