Spring循環(huán)依賴的解決

Spring循環(huán)依賴的解決

什么是循環(huán)依賴

循環(huán)依賴,是依賴關系形成了一個圓環(huán)。比如:A對象有一個屬性B,那么這時候我們稱之為A依賴B,如果這時候B對象里面有一個屬性A。那么這時候A和B的依賴關系就形成了一個循環(huán),這就是所謂的循環(huán)依賴。如果這時候IOC容器創(chuàng)建A對象的時候,發(fā)現(xiàn)B屬性,然后創(chuàng)建B對象,發(fā)現(xiàn)里面有A屬性,然后創(chuàng)建B.....這么無限循環(huán)下去。我們先用代碼演示一下:

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

運行一下結(jié)果

image-20200110220224411

那么我們可以看到循環(huán)依賴存在的問題了

  1. 棧內(nèi)存溢出
  2. 程序的維護性和擴展性太差

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

產(chǎn)生循環(huán)依賴產(chǎn)生的條件:

  1. 在容器中創(chuàng)建的對象是單例的
  2. 對象是循環(huán)依賴

精簡版解決方案

如果我們自己寫的話,該如何解決的呢?

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();//創(chuàng)建a對象
        B b = new B();//因為a對象依賴B,那么創(chuàng)建B
        b.setA(a);//創(chuàng)建B對象的時候,發(fā)現(xiàn)依賴A,那么把通過構(gòu)造方法生成的對象a賦值給B
         a.setB(b);//然后把生成的b對象注入到a里面
    }
}

Spring解決方案

當使用Spring的 @Autowired 注解的時候,其實Spring的實現(xiàn)原理和上面很相似,先通過生成相關的對象,然后再把里面需要依賴的對象設置進去。

我們現(xiàn)在從Spring源碼來走一遍。。

我們現(xiàn)貼出最基本的測試代碼

@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");
    }
}

在我們之前發(fā)布的SpringBoot啟動流程源碼分析里面,我們提到過bean單例的生成是在Spring容器創(chuàng)建過程中來完成的。經(jīng)過多層的調(diào)用,最終會調(diào)用到 doGetBean 這個方法里面。

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
   ...
   Object bean;
   //先從緩存中獲取是否定義了對應的類,這里的緩存包括了半成品類緩存(只生成了類,但是還沒有進行屬性注入的類)和成品類緩存(已經(jīng)完成了屬性注入的類)
   Object sharedInstance = getSingleton(beanName);
   if (sharedInstance != null && args == null) {
      ......
      //如果符合條件,直接從對飲給的bean單例中獲取到對象,然后返回
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }
   else {
      ...
      try {
         .....
         //創(chuàng)建單例bean,解決循環(huán)依賴的根本方案
         if (mbd.isSingleton()) {
            sharedInstance = getSingleton(beanName, () -> {
               try {
                  //調(diào)用創(chuàng)建單例的方法
                  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的創(chuàng)建
            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) {
            //創(chuàng)建beanDefinition所對應的的參數(shù)的bean實例,這里通過構(gòu)造方法或者工廠方法或者cglib創(chuàng)建了對象
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        if (earlySingletonExposure) {
            //將對象放到registeredSingletons隊列中,并從earlySingletonObjects中移除
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        ...
            //注入A的依賴,這里面會發(fā)現(xiàn)屬性,然后從doGetBean()方法開始,生成B對象,然后循環(huán)走到這里的時候,在隊列里面會同時存在A對象和B對象。然后B對象注入A成功,返回后將生成的B注入到A,此時完成了A和B的對象生成,并解決了循環(huán)依賴問題
            populateBean(beanName, mbd, instanceWrapper);
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        ...
}

加載過程比較長,其實主要是在加載的過程中將對象的創(chuàng)建過程進行了分類處理,在創(chuàng)建的不同時期,放入到隊列來進行區(qū)分。

  1. singletonObjects:單例對象列表
  2. singletonFactories:單例工廠隊列,對象剛開始創(chuàng)建的時候,會放入到這個隊列。
  3. earlySingletonObjects:產(chǎn)生了循環(huán)依賴的對象隊列,對象在創(chuàng)建之后,進行注入過程中,發(fā)現(xiàn)產(chǎn)生了循環(huán)依賴,那么會將對象放入到這個隊列,并且從singletonFactories中移除掉。
  4. singletonsCurrentlyInCreation:正在創(chuàng)建的對象隊列,整個創(chuàng)建過程都存放在這個隊列里面,當完成了所有的依賴注入以后,從這個隊列里面移除
  5. registeredSingletons:已經(jīng)創(chuàng)建成功的單例列表。

知道了這幾個隊列以后,我們可以來整理測試例子中,A和B對象是如何一步步創(chuàng)建,并解決其循環(huán)依賴的問題了。

  1. 首先,依次從singletonObjects,earlySingletonObjects,singletonFactories隊列中去尋找a對象,發(fā)現(xiàn)都沒有,返回了null。那么這時候就需要創(chuàng)建B對象
  2. a的創(chuàng)建的準備:在創(chuàng)建之前,將a放入到singletonsCurrentlyInCreation隊列,表明a正在進行創(chuàng)建。
  3. 開始創(chuàng)建a:通過反射創(chuàng)建對象a。
  4. 進行創(chuàng)建后的處理:創(chuàng)建a對象以后,將a放入到singletonFactories和registeredSingletons隊列,并從earlySingletonObjects中移除。然后進行依賴注入工作,發(fā)現(xiàn)有依賴B對象。
    1. 這時候進入了B對象的注入過程
    2. 首先,依次從singletonObjects,earlySingletonObjects,singletonFactories隊列中去尋找b對象,發(fā)現(xiàn)都沒有,返回了null。那么這時候就需要創(chuàng)建B對象
    3. b的創(chuàng)建的準備工作:在創(chuàng)建之前,將b放入到singletonsCurrentlyInCreation隊列,表明b正在進行創(chuàng)建
    4. 開始創(chuàng)建b:通過反射創(chuàng)建對象b。
    5. 進行創(chuàng)建后的處理:將b放入到singletonFactories和registeredSingletons隊列,并從earlySingletonObjects中移除。然后進行依賴注入工作,發(fā)現(xiàn)有依賴 A對象。
      1. 這時候進入A的注入過程。。。
      2. 從singletonObjects中查找a,發(fā)現(xiàn)a不存在但是singletonsCurrentlyInCreation隊列中有a,那么這時候說明a是在創(chuàng)建過程中的,此處又需要創(chuàng)建,屬于循環(huán)依賴了。然后去earlySingletonObjects查找,也沒發(fā)現(xiàn)。那么這時候去singletonFactories隊列中去尋找a對象,找到了。這時候?qū)對象放入到earlySingletonObjects隊列,并從singletonFactories中移除。因為發(fā)現(xiàn)了a對象,這里直接返回a,此時完成了b對象對A的依賴注入了
    6. b實例化完成,而且依賴也注入完成了,那么進行最后的處理。將b實例從singletonsCurrentlyInCreation隊列移除,表明b對象實例化結(jié)束。然后將b放入到singletonObjects和registeredSingletons隊列,并從singletonFactories和earlySingletonObjects隊列移除。最后將b對象注入到a對象中。然后a完成了創(chuàng)建過程。
  5. a實例化完成,而且依賴也注入完成了,那么進行最后的處理。將a實例從singletonsCurrentlyInCreation隊列移除,表明a對象實例化結(jié)束。然后將a放入到singletonObjects和registeredSingletons隊列,并從singletonFactories和earlySingletonObjects隊列移除。此時完成了a對象的創(chuàng)建。

總結(jié)

上述就是spring解決循環(huán)依賴的整體過程,跟我們之前的那個方法很相似,只是對于各種情況的處理更仔細。而且從這個過程也能理解spring對于對象的創(chuàng)建過程。

本文由 開了肯 發(fā)布!

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容