上一篇博客簡單地分析了下依賴注入。但是對于依賴注入的很多細節(jié),都沒有深入的分析。這一篇博客會繼續(xù)分析spring的依賴注入。這篇博客會解決分析getBean緩存時候遺留下來的循環(huán)依賴問題。
循環(huán)依賴分析
首先明確下,只有單例情況下,spring才會試著去解決循環(huán)依賴問題,多例是不會去解決循環(huán)依賴的。這個也好理解,如果是多例的話,比如a -> b 并且 b -> a 那么,當A a=new A(); 之后要注入b,b卻是多例的,那么究竟該注入哪個B是不確定的。如下圖:

接下來我們分析,為啥會有循環(huán)依賴的問題。
先來分析沒有循環(huán)依賴的問題
public static class A{
private B b;
//省略get和set方法
}
public static class B{
}
這個時候,如果spring先初始化A,然后會發(fā)現(xiàn)A依賴于B,然后就會初始化B,最后注入到A里。
整個流程用代碼表示大概如下所示:
A a = 創(chuàng)建A
B b = 創(chuàng)建B
-----> 創(chuàng)建A子流程 a.setB(b);
但是假設B也依賴了A呢?即
public static class B{
private A a;
}
那樣依賴注入過程就會變成(簡單示例)
A a = 創(chuàng)建A
B b = 創(chuàng)建B
----> 創(chuàng)建B子流程 b.setA(????); // 這個時候A還沒創(chuàng)建完成呢
a.setB(b);
那么如何解決呢?很簡單,把那個還沒創(chuàng)建完的A(只是new了,但是沒有進行依賴注入的A)set到B里就好了。

弄清了這個流程之后,我們再來分析spring是如何進行依賴注入的。
spring對引用類型的注入
這里我先從spring對引用屬性的注入開始。
<bean id="person" class="com.hdj.learn.spring.demo.Person">
<property name="car" ref="car"></property>
</bean>
即ref的注入
private Object resolveReference(Object argName, RuntimeBeanReference ref) {
try {
String refName = ref.getBeanName();
refName = String.valueOf(doEvaluate(refName));
if (ref.isToParent()) {
if (this.beanFactory.getParentBeanFactory() == null) {
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Can't resolve reference to bean '" + refName +
"' in parent factory: no parent factory available");
}
return this.beanFactory.getParentBeanFactory().getBean(refName);
}
else {
Object bean = this.beanFactory.getBean(refName);
this.beanFactory.registerDependentBean(refName, this.beanName);
return bean;
}
}
catch (BeansException ex) {
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
}
}
實現(xiàn)很簡單,就是從beanFactory里獲取要依賴的對象
我們再來回顧下流程

問題是,當走到6時候,似乎又會回到1,這樣不就死循環(huán)了么?
重點就是,第六步的獲取A,我們回到doGetBean方法中。
getSingleton 方法
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//已創(chuàng)建的對象里面找下
Object singletonObject = this.singletonObjects.get(beanName);
//沒找到,并且當前類正在被創(chuàng)建
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);
}
我們在分析初始化bean的緩存部分時,曾分析過這幾個緩存。當時其實只知道了singletonObjects是存儲了已經(jīng)創(chuàng)建了的對象。
現(xiàn)在讓我們回頭再看看這些緩存。
首先看看singletonFactories賦值的地方。
doCreateBean
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
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");
}
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
//默認實現(xiàn)返回bean
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
在創(chuàng)建bean時候,有這樣一段代碼,當需要進行提前暴露時候(當前創(chuàng)建對象單例 + 允許循環(huán)引用 + 當前類正在被創(chuàng)建)會調(diào)用addSingletonFactory方法
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);
}
}
}
這里就碰見了我們要分析的singletonFactories,它存儲了beanName -> 此處的匿名內(nèi)部類singletonFactory。
singletonFactory
我們再回到getSingleton方法,以及之前繪制的圖
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
邏輯很簡單

三級緩存
我們經(jīng)常聽說spring通過三級緩存解決了循環(huán)依賴,其實三級緩存非常簡單。就是指我們分析過的
Map<String, Object> singletonObjects
Map<String, ObjectFactory<?>> singletonFactories
Map<String, Object> earlySingletonObjects
這里分別舉這樣三個例子。
A 依賴 B(B不依賴A)

A 依賴 B && B依賴A

A依賴B && B依賴A + B依賴C && C 依賴 A

至此所謂的三級緩存及其使用,應該就非常的清楚了。
總結(jié)下
這篇博客分析了spring依賴注入中,循環(huán)依賴的問題,分析了在spring初始化bean時,各級緩存的作用。應該算是挺清晰的了。