Spring循環依賴的解決


Spring循環依賴的解決

什么是循環依賴

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

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

運行一下結果

image-20200110220224411

那么我們可以看到循環依賴存在的問題了

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

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

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

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

精簡版解決方案

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

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源碼來走一遍。。

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

@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 這個方法里面。

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

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

  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對於對象的創建過程。

本文由 開了肯 發布!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM