Spring循環依賴的解決

Spring循環依賴的解決

什麼是循環依賴

循環依賴,是依賴關係造成了一個圓環。好比:A對象有一個屬性B,那麼這時候咱們稱之爲A依賴B,若是這時候B對象裏面有一個屬性A。那麼這時候A和B的依賴關係就造成了一個循環,這就是所謂的循環依賴。若是這時候IOC容器建立A對象的時候,發現B屬性,而後建立B對象,發現裏面有A屬性,而後建立B.....這麼無限循環下去。咱們先用代碼演示一下:java

public class A {
    private B b=new B();
}

public class B {
    private A a=new A();
}

public class Test {
    public static void main(String[] args) {
        A a = new A();
    }
}

運行一下結果spring

image-20200110220224411

那麼咱們能夠看到循環依賴存在的問題了緩存

  1. 棧內存溢出
  2. 程序的維護性和擴展性太差

顯然這種思路是不正確的。app

產生循環依賴產生的條件:

  1. 在容器中建立的對象是單例的
  2. 對象是循環依賴

精簡版解決方案

若是咱們本身寫的話,該如何解決的呢?ide

public class A {
    private B b;
    public void setB(B b) {
        this.b = b;
    }
}
public class B {
    private A a;
    public void setA(A a) {
        this.a = a;
    }
}
public class Test {
    public static void main(String[] args) {
        A a = new A();//建立a對象
        B b = new B();//由於a對象依賴B,那麼建立B
        b.setA(a);//建立B對象的時候,發現依賴A,那麼把經過構造方法生成的對象a賦值給B
         a.setB(b);//而後把生成的b對象注入到a裏面
    }
}

Spring解決方案

當使用Spring的 @Autowired 註解的時候,其實Spring的實現原理和上面很類似,先經過生成相關的對象,而後再把裏面須要依賴的對象設置進去。源碼分析

咱們如今從Spring源碼來走一遍。。測試

咱們現貼出最基本的測試代碼ui

@Component
public class A {
   @Autowired
   B b;
}
@Component
public class B {
    @Autowired
    A a;
}
public class RecyclerTest {
    @Test
    public void test() {
        ApplicationContext context = new AnnotationConfigApplicationContext("com.kailaisi.demo.recycler");
        //getbean得時候才進行IOC容器中的對象的實例化工做
        A a = (A) context.getBean("a");
    }
}

在咱們以前發佈的SpringBoot啓動流程源碼分析裏面,咱們提到過bean單例的生成是在Spring容器建立過程當中來完成的。通過多層的調用,最終會調用到 doGetBean 這個方法裏面。this

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
   ...
   Object bean;
   //先從緩存中獲取是否認義了對應的類,這裏的緩存包括了半成品類緩存(只生成了類,可是尚未進行屬性注入的類)和成品類緩存(已經完成了屬性注入的類)
   Object sharedInstance = getSingleton(beanName);
   if (sharedInstance != null && args == null) {
      ......
      //若是符合條件,直接從對飲給的bean單例中獲取到對象,而後返回
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }
   else {
      ...
      try {
         .....
         //建立單例bean,解決循環依賴的根本方案
         if (mbd.isSingleton()) {
            sharedInstance = getSingleton(beanName, () -> {
               try {
                  //調用建立單例的方法
                  return createBean(beanName, mbd, args);
               }
               ...
            });
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
         }
   ...
   return (T) bean;
}

    @Override
    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {
            ...
            //進行bean的建立
            Object beanInstance = doCreateBean(beanName, mbdToUse, args);
            ...
    }
    
    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {
        //bean的包裝類
        BeanWrapper instanceWrapper = null;
        ...
        if (instanceWrapper == null) {
            //建立beanDefinition所對應的的參數的bean實例,這裏經過構造方法或者工廠方法或者cglib建立了對象
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        if (earlySingletonExposure) {
            //將對象放到registeredSingletons隊列中,並從earlySingletonObjects中移除
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        ...
            //注入A的依賴,這裏面會發現屬性,而後從doGetBean()方法開始,生成B對象,而後循環走到這裏的時候,在隊列裏面會同時存在A對象和B對象。而後B對象注入A成功,返回後將生成的B注入到A,此時完成了A和B的對象生成,並解決了循環依賴問題
            populateBean(beanName, mbd, instanceWrapper);
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        ...
}

加載過程比較長,其實主要是在加載的過程當中將對象的建立過程進行了分類處理,在建立的不一樣時期,放入到隊列來進行區分。code

  1. singletonObjects:單例對象列表
  2. singletonFactories:單例工廠隊列,對象剛開始建立的時候,會放入到這個隊列。
  3. earlySingletonObjects:產生了循環依賴的對象隊列,對象在建立以後,進行注入過程當中,發現產生了循環依賴,那麼會將對象放入到這個隊列,而且從singletonFactories中移除掉。
  4. singletonsCurrentlyInCreation:正在建立的對象隊列,整個建立過程都存放在這個隊列裏面,當完成了全部的依賴注入之後,從這個隊列裏面移除
  5. registeredSingletons:已經建立成功的單例列表。

知道了這幾個隊列之後,咱們能夠來整理測試例子中,A和B對象是如何一步步建立,並解決其循環依賴的問題了。

  1. 首先,依次從singletonObjects,earlySingletonObjects,singletonFactories隊列中去尋找a對象,發現都沒有,返回了null。那麼這時候就須要建立B對象
  2. a的建立的準備:在建立以前,將a放入到singletonsCurrentlyInCreation隊列,代表a正在進行建立。
  3. 開始建立a:經過反射建立對象a。
  4. 進行建立後的處理:建立a對象之後,將a放入到singletonFactories和registeredSingletons隊列,並從earlySingletonObjects中移除。而後進行依賴注入工做,發現有依賴B對象。
    1. 這時候進入了B對象的注入過程
    2. 首先,依次從singletonObjects,earlySingletonObjects,singletonFactories隊列中去尋找b對象,發現都沒有,返回了null。那麼這時候就須要建立B對象
    3. b的建立的準備工做:在建立以前,將b放入到singletonsCurrentlyInCreation隊列,代表b正在進行建立
    4. 開始建立b:經過反射建立對象b。
    5. 進行建立後的處理:將b放入到singletonFactories和registeredSingletons隊列,並從earlySingletonObjects中移除。而後進行依賴注入工做,發現有依賴 A對象。
      1. 這時候進入A的注入過程。。。
      2. 從singletonObjects中查找a,發現a不存在可是singletonsCurrentlyInCreation隊列中有a,那麼這時候說明a是在建立過程當中的,此處又須要建立,屬於循環依賴了。而後去earlySingletonObjects查找,也沒發現。那麼這時候去singletonFactories隊列中去尋找a對象,找到了。這時候將a對象放入到earlySingletonObjects隊列,並從singletonFactories中移除。由於發現了a對象,這裏直接返回a,此時完成了b對象對A的依賴注入了
    6. b實例化完成,並且依賴也注入完成了,那麼進行最後的處理。將b實例從singletonsCurrentlyInCreation隊列移除,代表b對象實例化結束。而後將b放入到singletonObjects和registeredSingletons隊列,並從singletonFactories和earlySingletonObjects隊列移除。最後將b對象注入到a對象中。而後a完成了建立過程。
  5. a實例化完成,並且依賴也注入完成了,那麼進行最後的處理。將a實例從singletonsCurrentlyInCreation隊列移除,代表a對象實例化結束。而後將a放入到singletonObjects和registeredSingletons隊列,並從singletonFactories和earlySingletonObjects隊列移除。此時完成了a對象的建立。

總結

上述就是spring解決循環依賴的總體過程,跟咱們以前的那個方法很類似,只是對於各類狀況的處理更仔細。並且從這個過程也能理解spring對於對象的建立過程。

本文由 開了肯 發佈!

相關文章
相關標籤/搜索