循環依賴
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) { A a = new A(); a.initialze(); singletonObjects.put(name, a); return a;}
A 依賴 B 的情況下的加載流程
偽代碼如下:
public Object getBean(String name) { A a = new A(); a.setB(getBean("B")); a.initialze(); singletonObjects.put(name, 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; Object sharedInstance = getSingleton(beanName); if(sharedInstance != null) { bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } }); else if (mbd.isPrototype()) { 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) { Object singletonObject = this.singletonObjects.get(beanName); 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;}
3、 第 1 步中 單例 bean 的加載邏輯
sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; }});
public 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) { beforeSingletonCreation(beanName); singletonObject = singletonFactory.getObject(); afterSingletonCreation(beanName); addSingleton(beanName, singletonObject); } return singletonObject; }}
4、核心方法,加載 bean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = instanceWrapper.getWrappedInstance(); Class<?> beanType = instanceWrapper.getWrappedClass();
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
Object exposedObject = bean; try { populateBean(beanName, mbd, instanceWrapper); exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { throw ex; }
if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { 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."); } } } }
try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); }
return exposedObject;}
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、刪除二級緩存和三級緩存