上一篇部落格簡單地分析了下依賴注入。但是對于依賴注入的很多細節,都沒有深入的分析。這一篇部落格會繼續分析spring的依賴注入。這篇部落格會解決分析getBean緩存時候遺留下來的循環依賴問題。
循環依賴分析
首先明确下,隻有單例情況下,spring才會試着去解決循環依賴問題,多例是不會去解決循環依賴的。這個也好了解,如果是多例的話,比如a -> b 并且 b -> a 那麼,當A a=new A(); 之後要注入b,b卻是多例的,那麼究竟該注入哪個B是不确定的。如下圖:
接下來我們分析,為啥會有循環依賴的問題。
先來分析沒有循環依賴的問題
public static class A{
private B b;
//省略get和set方法
}
public static class B{
}
這個時候,如果spring先初始化A,然後會發現A依賴于B,然後就會初始化B,最後注入到A裡。
整個流程用代碼表示大概如下所示:
A a = 建立A
B b = 建立B
-----> 建立A子流程 a.setB(b);
但是假設B也依賴了A呢?即
public static class B{
private A a;
}
那樣依賴注入過程就會變成(簡單示例)
A a = 建立A
B b = 建立B
----> 建立B子流程 b.setA(????); // 這個時候A還沒建立完成呢
a.setB(b);
那麼如何解決呢?很簡單,把那個還沒建立完的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);
}
}
實作很簡單,就是從beanFactory裡擷取要依賴的對象
我們再來回顧下流程
問題是,當走到6時候,似乎又會回到1,這樣不就死循環了麼?
重點就是,第六步的擷取A,我們回到doGetBean方法中。
getSingleton 方法
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);
}
我們在分析初始化bean的
緩存部分時,曾分析過這幾個緩存。當時其實隻知道了singletonObjects是存儲了已經建立了的對象。
現在讓我們回頭再看看這些緩存。
首先看看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 {
//預設實作傳回bean
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
在建立bean時候,有這樣一段代碼,當需要進行提前暴露時候(目前建立對象單例 + 允許循環引用 + 目前類正在被建立)會調用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 -> 此處的匿名内部類singletonFactory。
singletonFactory
我們再回到getSingleton方法,以及之前繪制的圖
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
邏輯很簡單
三級緩存
我們經常聽說spring通過三級緩存解決了循環依賴,其實三級緩存非常簡單。就是指我們分析過的
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
二級緩存
至此所謂的三級緩存及其使用,應該就非常的清楚了。
總結下
這篇部落格分析了spring依賴注入中,循環依賴的問題,分析了在spring初始化bean時,各級緩存的作用。應該算是挺清晰的了。