循環(huán)依賴
所謂循環(huán)依賴,是指在spring中,類A
中有一個(gè)成員變量為類B
,而類B
中也有一個(gè)成員變量為類A
。這會(huì)造成在spring初始化容器中的bean時(shí),bean A的初始化需要bean B的初始化的完成,而bean B的初始化又需要bean A初始化的完成。
A與B間形成了循環(huán)依賴:
循環(huán)依賴的解決
spring中,循環(huán)依賴的解決是有條件的:僅限于singleton(單例)作用域的bean,且依賴的成員變量不是在構(gòu)造函數(shù)中初始化的。
不能解決的情況一:
原型prototype
作用域的bean發(fā)生循環(huán)依賴會(huì)直接拋出異常。
/**AbstractBeanFactory.doGetBean()方法*/
// 如果原型作用域的bean在獲取時(shí),發(fā)現(xiàn)正在創(chuàng)建中
// 即發(fā)生了循環(huán)依賴,直接拋出異常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
這種情況好理解,原型的bean如果允許循環(huán)依賴,那一定會(huì)處在不斷創(chuàng)建依賴類的循環(huán)中,最終導(dǎo)致堆溢出
不能解決的情況二:
單例bean,但依賴的成員變量在構(gòu)造函數(shù)中初始化。如:
public class A {
private B b;
public A(B b) {
this.b = b;
}
}
public class B {
@Autowired
private A a;
}
//配置文件:
<bean id="a" class="test.spring.bean.A" >
<constructor-arg ref="b" index="0" />
</bean>
<bean id="b" class="test.spring.bean.B" />
簡(jiǎn)單說(shuō)明一下原因,spring中的循環(huán)依賴的解決關(guān)鍵在于:
????緩存 實(shí)例化成功,但還沒(méi)注入成員變量的bean,等發(fā)生循環(huán)依賴時(shí),直接引用緩存中只實(shí)例化過(guò)的bean,這樣就不用等待依賴bean繼續(xù)注入成員變量,然后發(fā)生循環(huán)依賴。
如果實(shí)例化過(guò)程中就發(fā)生了循環(huán)依賴,還沒(méi)來(lái)得及緩存,那就只有拋出異常了。
循環(huán)依賴的解決:
當(dāng)我們調(diào)用context.getBean()
方法來(lái)獲取一個(gè)單例bean時(shí),如果這個(gè)bean在容器中還沒(méi)有,會(huì)進(jìn)行創(chuàng)建(AbstractAutowireCapableBeanFactory.createBean()
)。
創(chuàng)建這個(gè)bean的過(guò)程簡(jiǎn)單可分為兩步:實(shí)例化(instantiate,使用構(gòu)造函數(shù)創(chuàng)建bean對(duì)象)和初始化(initialize,注入bean的成員變量):
1. 存入map緩存
當(dāng)實(shí)例化完后,會(huì)將構(gòu)造好的bean對(duì)象存在singletonFactories
緩存map中,見(jiàn)AbstractAutowireCapableBeanFactory.doCreateBean()
:
// 如果bean為單例且允許循環(huán)依賴,將該bean的singletonFactory存在map中暴露
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//重點(diǎn)!!!
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
addSingletonFactory()
方法中,beanName-ObjectFactory
這樣的key-value
被放進(jìn)了singletonFactories
map緩存集合。
這里注意,實(shí)例化的bean并不是直接存入緩存map,而是通過(guò)ObjectFactory
工廠對(duì)象被暴露在了緩存里,后面循環(huán)依賴時(shí),也是以ObjectFactory
形式被取出。然后調(diào)用ObjectFactory.getObject()
——》getEarlyBeanReference()
方法,來(lái)獲取到被SmartInstantiationAwareBeanPostProcessor
后處理器處理過(guò)的剛剛實(shí)例化結(jié)束的bean對(duì)象。
這里的ObjectFactory
發(fā)生過(guò)替換。bean在第一次獲取時(shí),生成的ObjectFactory對(duì)象,getObject()
方法實(shí)際上會(huì)調(diào)用createBean()
方法創(chuàng)建bean。而bean在創(chuàng)建的過(guò)程中,實(shí)例化完成后會(huì)重新創(chuàng)建ObjectFactory對(duì)象,getObject()
方法會(huì)變成調(diào)用getEarlyBeanReference()
,用于獲取的緩存的實(shí)例化的bean。
2. 循環(huán)依賴時(shí),從緩存中取出
當(dāng)實(shí)例化bean A存入緩存后,我們初始化A ,這時(shí)會(huì)注入依賴類B(getBean()
方法)。
依賴類B注入時(shí)進(jìn)行創(chuàng)建,實(shí)例化——》初始化時(shí),又循環(huán)依賴了bean A,但這時(shí),緩存中已經(jīng)保存了實(shí)例化的bean A,我們通過(guò)getSingleton()
方法直接獲取A對(duì)象注入后返回。循環(huán)依賴被破解。
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// 從緩存中獲取單例bean,緩存中的bean只被實(shí)例化,還未初始化
Object sharedInstance = getSingleton(beanName);
//獲取成功,直接返回bean
if (sharedInstance != null && args == null) {
…………
}
………………
}
//從singletonFactories中獲取該beanName的ObjectFactory,然后通過(guò)ObjectFactory獲取bean
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. 整個(gè)流程
最后梳理下整個(gè)循環(huán)依賴解決的流程,以上面的A、B為例:
-
context.getBean(A.class)
獲取容器中的單例A,此時(shí)bean A對(duì)象還不存在,進(jìn)行創(chuàng)建。 - 實(shí)例化bean A,將其放入緩存
- 初始化A,注入依賴類B
- 通過(guò)
getBean(B.class)
獲取依賴類B,發(fā)現(xiàn)bean B在容器中不存在,進(jìn)行創(chuàng)建 - 實(shí)例化B,將其放入緩存
- 初始化B,注入依賴類A
- 通過(guò)
getBean(A.class)
獲取依賴類A,此時(shí)發(fā)現(xiàn)緩存中已經(jīng)存在實(shí)例化的A,獲取后返回 - B初始化成功,返回
- A初始化成功, 返回bean A
注意:整個(gè)流程結(jié)束后,B持有的也不再是實(shí)例化的A,而是初始化結(jié)束的A。