循環依賴
Spring 有一個經典的問題,就是如何解決循環依賴,話不多說,直接開始,
@Component
public Class A {
@Autowired private B b;
}
@Component
public 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、刪除二級緩存和三級緩存