- 什么是循環依賴?
先看代碼:
public class A {
private B b;
// 省略set/get方法
}
public class B {
private A a;
// 省略set/get方法
}
可以看到A類里有一個屬性是B類對象,而B類里也有一個屬性是A類對象,則我們可以稱A類對象與B類對象之間互相循環依賴。然后我們對把這倆個類納入到IOC容器中進行管理,現在進行xml配置:
<bean id="a" class="com.A">
<property name="b" ref="b"/>
</bean>
<bean id="b" class="com.B">
<property name="a" ref="a"/>
</bean>
當配置好xml以后,我們創建容器,并且調用getBean方法來獲取某個對象,那么會發生什么事情呢?正常邏輯應該是發生了死循環,a對象的創建需要依賴b對象,而b對象的創建同時也需要a對象。這簡直就是沒辦法解決嘛!但是SpringIOC卻解決了這個問題,并且你可以正常的獲取到相應的對象而不會發生錯誤。
那么SpringIOC是如何解決循環依賴的問題呢?
原理
SpringIOC解決循環依賴的思路就是依靠緩存,同時還得引出個概念即早期暴露引用。我們知道在IOC容器里bean的初始化的過程分為三個步驟:創建實例、屬性注入實例、回調實例實現的接口方法。解決思路就在這:當我們創建實例與屬性注入實例這倆個步驟之間的時候,我們引入緩存,將這些已經創建好但是并沒有注入屬性的實例放到緩存里,而這些放在緩存里但是沒有被注入屬性的實例對象,就是解決循環依賴的方法,打個比方:A對象的創建需要引用到B對象,而B對象的創建也需要A對象,而此時當B對象創建的時候直接從緩存里引用A對象(雖然不是完全體A對象,畢竟沒有賦值處理),當B對象完成創建以后再被A對象引用進去,則A對象也完成了創建。
這就是SpringIOC解決bean直接循環依賴的思路當然有一個小問題,IOC能夠解決的只能是屬性之間的循環依賴,如果有bean之間的構造器相互依賴則就解決不了只能報錯了。
- 我們現在來看看Spring IOC的源碼
先看一下下面介紹源碼里的緩存的表:
源碼 | 級別 | 描述 |
---|---|---|
singletonObjects | 一級緩存 | 用于存放完全初始化好的 bean,從該緩存中取出的 bean 可以直接使用 |
earlySingletonObjects | 二級緩存 | 存放原始的 bean 對象(尚未填充屬性),用于解決循環依賴 |
singletonFactories | 三級緩存 | 存放 bean 工廠對象,用于解決循環依賴 |
省略不必要的代碼
protected <T> T doGetBean( final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
Object bean;
// 從緩存中取得bean的實例
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
//進行后續處理,如果是正常的普通bean則返回普通的bean,如果是實現了FactoryBean接口的bean則返回的是getObject里的內容
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// 解決依賴的問題,這個跟我們說的依賴是不一樣的.可以忽略
// ......
// 創建單例 bean
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// 發生異常,銷毀bean
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// Check if required type matches the type of the actual bean instance.
// ......
return (T) bean;
}
以上是doGetBean方法里的代碼,當然我省略了跟本章無關的代碼。
一步步來吧,先進行初始化a對象的操作,然后發現調用的是createBean(String beanName, RootBeanDefinition mbd, Object[] args)方法,而真正起作用的是doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)方法。而在這個方法里面包含了三個重要的方法createBeanInstance、populateBean、initializeBean,看過之前系列文章的人都知道這三個方法分別代表:創建實例、屬性注入、方法回調,這是bean初始化的核心方法。當然下面這段代碼是在createBeanInstance和populateBean中間的一段doCreateBean的代碼。
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// ......
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
這段代碼在spring源碼注釋里描述是用來解決循環依賴的問題的。包含了一個匿名內部類ObjectFactory<T>(普通的工廠類返回的是getObject方法返回的對象),用getEarlyBeanReference實現了getObject方法。同時還調用了addSingletonFactory方法。分別來看一下各自方法的實現:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
if (exposedObject == null) {
return null;
}
}
}
}
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);
}
}
}
可以看到在addSingletonFactory方法中,會將beanName與singletonFactory形成kv關系put進singletonFactories里面。并且將earlySingletonObjects里面的key值為beanName的kv進行移除。
此時a對象的早期暴露引用已經存在了singletonFactories三級緩存里面。此時a對象進行populateBean方法進行屬性注入,發現需要依賴b對象,緊接著就是去初始化b對象。繼續重復上面的步驟到b對象進行屬性注入這一步的時候(此時singletonFactories三級緩存里已經有了a對象的提前暴露引用和b對象的提前暴露引用的工廠對象),發現需要依賴a對象,此時去獲取a對象,看代碼:
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
//繼續看這個方法
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 != NULL_OBJECT ? singletonObject : null);
}
先從singletonObjects一級緩存里取,如果沒有取到,則從earlySingletonObjects二級緩存里取,如果還是沒取到,則從singletonFactories三級緩存里取,取到以后進行getObject方法返回早期暴露對象引用,同時放進earlySingletonObjects二級緩存里,并且三級緩存里進行刪除該kv。
那么到此,a對象的早期暴露引用已經被b對象獲取到了,并且在singletonFactories三級緩存里已經沒有a對象的早期暴露引用的工廠對象了,a對象的早期暴露引用存在了二級緩存earlySingletonObjects里面,當然singletonFactories三級緩存依然有b對象的早期暴露引用的工廠對象。
繼續:b對象拿到了a對象的早期暴露引用,進行完屬性注入以后,則返回一個b對象了同時調用方法getSingleton(String beanName, ObjectFactory<?> singletonFactory),看源碼:
//我已經刪除了很多無關的代碼
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
// ......
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// ......
beforeSingletonCreation(beanName);
boolean newSingleton = false;
// ... ...
//... ...
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
finally {
// ... ...
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}
其實就是倆個方法:singletonObject = singletonFactory.getObject();和addSingleton(beanName, singletonObject);至此我們不需要說明第一個了,著重來看一下addSingleton方法。
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
ok,上面源碼已經說明了此時singletonObjects一級緩存將要存入b對象,而二級緩存earlySingletonObjects和三級緩存singletonFactories則把相關緩存的對象移除。至此b對象則只存在一級緩存singletonObjects里面了。
當b對象完成了初始化以后,a對象則進行相關屬性的注入引入b的對象。完成實例化的同時a對象也會調用一次addSingleton方法,那么a對象完成以后,也就只有一級緩存singletonObjects里面才有a對象。
至此,屬性的循環依賴問題則完美的得到解決。
- 文末
感謝 【減肥是生命的主旋律】 的提問和回答
有一個小問題,為什么在解決循環依賴問題的時候,我們會用到三級緩存singletonFactories呢?感覺二級緩存earlySingletonObjects就可以解決問題了呢?
那么答案就在這里:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
if (exposedObject == null) {
return null;
}
}
}
}
return exposedObject;
}
在將三級緩存放入二級緩存的時候,會判斷是否有SmartInstantiationAwareBeanPostProcessor這樣的后置處理器,換句話說這里是給用戶提供接口擴展的,所以采用了三級緩存。