Spring 循環依賴及解決方式


循環依賴

Spring 有一個經典的問題,就是如何解決循環依賴,話不多說,直接開始, 

@Componentpublic Class A {   @Autowired private B b;}@Componentpublic Class B {   @Autowired private A b;}

spring bean 的生命周期

獲取一個 Bean 的操作從 getBean(String name) 開始主要步驟為

1、getBean(String name)

2、實例化對象 A a = new A(); 此時執行構造方法的依賴注入

3、設置對象屬性 populateBean(beanName, mbd, instanceWrapper); 此時執行屬性的依賴注入

4、執行初始化方法 initializeBean(beanName, exposedObject, mbd); 此時執行 bean 的 initialize 方法

5、將生成好的 bean 對象添加到 單例池(一個 hashMap,保證單例 bean 在 context 僅僅存在一個對象)

6、結束

偽代碼如下:

public Object getBean(String name) {	//省略根據name獲取A的過程	A a = new A();	a.initialze();	singletonObjects.put(name, a);	return a;}
A 依賴 B 的情況下的加載流程

 

偽代碼如下:

public Object getBean(String name) {	//省略根據name獲取A的過程	A a = new A(); //實例化A	a.setB(getBean("B")); //設置屬性,發現a依賴於b,所以先加載b,加載B完成以后再繼續加載a	a.initialze(); //執行初始化方法	singletonObjects.put(name, a); //將a放入單例池中	return a;}
A、B 互相依賴的加載流程

 

以上就會出現一個問題,由於 a、b 都是單例 Bean,加載 b 的時候,到了上圖中標紅的階段后,b 依賴注入的 a 的引用應該是通過 getBean(A) 得到的引入,如果還是以上的邏輯,又再一次走入了 A 的創建邏輯,此時就是發生了循環依賴。下面我們就開始介紹 Spring 是如何解決循環依賴的。

一級緩存:單例池 singletonObjects

private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
 

我們都知道如果是單例的 Bean,每次 getBean(beanName)返回同一個 bean,也就是在整個 ApplicationContext 里面,僅有一個單例 Bean,單例 Bean 創建完成后就放在 singletonObjects 這個 Map 里面,這就是一級緩存。此時說的“創建完成”指的是圖一的第 6 步驟,圖三中 getBean("B") 的過程中,a 是沒有加入到一級緩存中,所以在 getBean("B") 的流程中,b 依賴了 a,此時 b 是找不到 a 對象的。依然會無法解決循環引用的問題。

 

二級緩存:earlySingletonObjects

private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

這個時候我們考慮再引入一個 Map 存放引用,earlySingletonObjects 這個 map 我們打算存放提前暴露 bean 的引用,實例化以后,我們就把對象放入到 earlySingletonObjects 這個 map 中,這樣在 加載 b 的過程中,b.setA(getBean("a")),我們就可以在 earlySingletonObjects 拿到 a 的引用,此時 a 僅僅經過了實例化,並沒有設置屬性。流程如下:

 

1、getBean(A)

2、A a = new A();

3、earlySingletonObjects.put("a", a); 將 A 放入二級緩存

3、設置 A 的屬性

4、getBean(B)

5、設置 B 的屬性,發現 B 依賴 A,從二級緩存中獲取 A

6、加載 B 成功

7、將 B 放入一級緩存

8、繼續加載 A

9、加載 A 完成,將 A 放入單例池

 

到目前為止,發現使用二級緩存似乎就能解決我們的問題。看起來很美好,這是 Spring IOC 的特性,Spring 的另一大特性是 AOP 面向切面編程,動態增強對象,不管使用 JDK 的動態代理和 Cglib 動態代理,都會生成一個全新的對象。下圖中我標出了 AOP 動態增強的位置。

 

此時就會出現一個問題,因為經過 AOP 以后,生成的是增強后的 bean 對象,也就是一個全新的對象,java培訓我們可以看到經過圖中的流程后,單例池中會存在兩個 bean:增強后的 a、b 對象,此時 a 對象中依賴的 b 為增強后的,而 b 對象依賴的 a 是為原始對象,未增強的。所以使用二級緩存解決不了循環依賴中發生過 aop 的引用問題。

三級緩存:singletonFactories

private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

為了解決二級緩存中 AOP 生成新對象的問題,Spring 中的解決方案是:提前 AOP,如果我們能夠提前 AOP 就能解決上面的問題了,提前 AOP 指的就是,在 加載 B 的流程中,如果發生了循環依賴,就是說 b 又依賴了 a,我們就要對 a 執行 aop,提前獲取增強以后的 a 對象,這樣 b 對象依賴的 a 對象就是增強以后的 a 了。三級緩存的 key 是 beanName,value 是一個 lambda 表達式,這個 lambda 表達式的作用就是進行提前 AOP。

 

下面是加入了三級緩存和 AOP 的流程圖,PS:可能會有點亂。。。。。。

 

上面就是三級緩存的作用,其中有個三級緩存到二級緩存的升級過程,這個非常重重要,這個主要是防止重復 aop。好的,寫到這里,我們對 Spring 如何使用三級緩存解決循環依賴的流程已經大概清楚了,下面分析一下源碼。

源碼解析:

1、 doGetBean
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {    Object bean;    //首先先嘗試獲取bean,如果加載過就不會在重復加載了    Object sharedInstance = getSingleton(beanName);    //省略細節    if(sharedInstance != null) {        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);    } else {         //根據beanName獲取 beanDefinition 對象        final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);        if (mbd.isSingleton()) {            //單例bean的加載邏輯           sharedInstance = getSingleton(beanName, () -> {              try {                 return createBean(beanName, mbd, args);              }              catch (BeansException ex) {                 // Explicitly remove instance from singleton cache: It might have been put there                 // eagerly by the creation process, to allow for circular reference resolution.                 // Also remove any beans that received a temporary reference to the bean.                 destroySingleton(beanName);                 throw ex;              }           });           else if (mbd.isPrototype()) {               //原型域bean的加載邏輯               Object prototypeInstance = null;               try {                  beforePrototypeCreation(beanName);                  prototypeInstance = createBean(beanName, mbd, args);               }               finally {                  afterPrototypeCreation(beanName);               }               bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);            }           bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);        }    }    return (T)bean;}
2、 第 1 步中 getSingleton(beanName)
 
public Object getSingleton(String beanName) {   return getSingleton(beanName, true);}
protected Object getSingleton(String beanName, boolean allowEarlyReference) { //首先去一級緩存中獲取如果獲取的到說明bean已經存在,直接返回 Object singletonObject = this.singletonObjects.get(beanName); //如果一級緩存中不存在,則去判斷該bean是否在創建中,如果該bean正在創建中,就說明了,這個時候發生了循環依賴 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { //如果發生循環依賴,首先去二級緩存中獲取,如果獲取到則返回,這個地方就是獲取aop增強以后的bean singletonObject = this.earlySingletonObjects.get(beanName); //如果二級緩存中不存在,且允許提前訪問三級引用 if (singletonObject == null && allowEarlyReference) { //去三級緩存中獲取 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { //如果三級緩存中的lambda表達式存在,執行aop,獲取增強以后的對象,為了防止重復aop,將三級緩存刪除,升級到二級緩存中 singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject;}
3、 第 1 步中 單例 bean 的加載邏輯
sharedInstance = getSingleton(beanName, () -> {  try {     return createBean(beanName, mbd, args);  }  catch (BeansException ex) {     // Explicitly remove instance from singleton cache: It might have been put there     // eagerly by the creation process, to allow for circular reference resolution.     // Also remove any beans that received a temporary reference to the bean.     destroySingleton(beanName);     throw ex;  }});
//獲取beanpublic Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "Bean name must not be null"); synchronized (this.singletonObjects) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { //將當前bean加入到 singletonsCurrentlyInCreation 這個map中,這個map里面是正在創建中的bean,用於判斷循環依賴 beforeSingletonCreation(beanName); //執行上面方法的lambda表達式,創建bean singletonObject = singletonFactory.getObject(); //將 singletonsCurrentlyInCreation 里面的這個bean刪除 afterSingletonCreation(beanName); //bean創建完成,將bean加入到單例池中 addSingleton(beanName, singletonObject); } return singletonObject; }}
4、核心方法,加載 bean
 
//createBean(beanName, mbd, args); 方法 創建bean的核心邏輯// 最終調用的是 AbstractAutowiredCapableBeanFactory.createBean 這個方法protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)      throws BeanCreationException {   // Instantiate the bean.   BeanWrapper instanceWrapper = null;   if (mbd.isSingleton()) {      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);   }   if (instanceWrapper == null) {      instanceWrapper = createBeanInstance(beanName, mbd, args);   }   //實例化,操作等同於 new 一個bean對象   final Object bean = instanceWrapper.getWrappedInstance();   Class<?> beanType = instanceWrapper.getWrappedClass();
//是否允許提前暴露對象,如果當前bean為單例,且允許循環引用,與當前bean正在創建中,則允許提前暴露 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { //將當前bean放入三級緩存中 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
// Initialize the bean instance. Object exposedObject = bean; try { //開始設置屬性,當前bean依賴於其它bean,則需要 doGetBean 創建以來的bean,如果依賴的bean不存在,則首先創建依賴的bean,循環依賴發生的位置 populateBean(beanName, mbd, instanceWrapper); //執行初始化方法和aop增強,此時如果有aop,exposedObject就是增強以后的對象了,但是有一點需要注意,如果提前執行了aop,則exposedObject不會再次執行aop了 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { throw ex; }
if (earlySingletonExposure) { //這個我們可以看上面getSingleton方法,該方法的參數為false,就說明只允許去一級緩存和二級緩存獲取,此時bean在創建中,一級緩存一定沒有,就看二級緩存能不能獲取到了 Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { //如果二級緩存獲取到了,則說明提前執行了aop if (exposedObject == bean) { //exposedObject就是要放入單例池中的對象,如果提前執行了aop,則將exposedObject對象替換為aop以后的對象 //這個地方可能有一些疑問,exposedObject是原始對象執行過 依賴注入,而earlySingletonReference是提前執行aop的對象,沒有執行過依賴注入,是不是有什么問題呢? //答案是不會,因為earlySingletonReference作為exposedObject的增強對象,內部是持有原對象的引用的 exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } } } }
// Register bean as disposable. try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); }
return exposedObject;}
//下面我們看一下 上面方法中的 將bean放入三級緩存提前暴露的方法//addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }}

 

通過對上面源碼的解析,看到了 bean 加載的整個生命周期,和三級緩存的作用。

比如:

1、如何判斷是否存在循環依賴:使用 isSingletonCurrentlyInCreation(beanName) 這個方法

2、三級緩存如何升級到二級緩存的:參考第二步

3、提前執行 AOP:這個東西是在 獲取三級緩存的時候執行的,里面有一個 getEarlyBeanReference(beanName, mbd, bean) 這個 lambda 表達式,這個方法就是提前執行 aop,具體可以參考 AbstractAutoProxyCreator

4、如果提前執行 AOP,則需要替換原對象

文字總結 A、B 循環依賴

1、getBean(A) 先去單例池獲取,單例池不存在,二級緩存獲取,二級緩存不存在且允許提前訪問,三級緩存中取,此時返回為空,開始加載 A

2、singletonsCurrentlyInCreation(A) 將 A 放入正在創建的 Map 中

3、new A(); 實例化 A

4、提前暴露 A,將 A 放入三級緩存,addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

5、設置屬性 populateBean(beanName, mbd, instanceWrapper);

6、發現 A 依賴 B,需要先創建 B

7、getBean(B)

8、先去單例池獲取 B,單例池不存在,二級緩存獲取,二級緩存不存在且允許提前訪問,三級緩存中取,此時返回為空,開始加載 B

9、將 B 放入 singletonsCurrentlyInCreation() 的 Map 中

10、new B() 實例化 B

11、將 B 放入三級緩存 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

12、設置屬性 populateBean(beanName, mbd, instanceWrapper);

13、發現 B 依賴 A

14、getBean(A)

15、發現三級緩存中存在 A,getEarlyBeanReference(A, mbd, bean) 獲取 A,同時把 A 放入二級緩存,刪除三級緩存

16、執行 B 的 initializeBean 方法,執行 aop,獲取增強以后的引用

17、B 創建完了,將 B 放入單例池沖

18、繼續執行第 7 步,返回的 getBean(B)就是創建好的 B

19、接下來 A 初始化

20、因為 A 的三級緩存中的 getEarlyBeanReference(beanName, mbd, bean) 被 B 已經執行過了

21、A 就能從二級緩存中獲取自己的引用

22、如果發現引用變了,此時 A 就指向二級緩存中的引用

23、將 A 放出單例池中

24、刪除二級緩存和三級緩存


免責聲明!

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



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