這版有錯誤 遞歸依賴加載並非在這個循環裏面,而是在填充屬性的時候進行加載的。spring
循環依賴定義緩存
在spring的啓動過程當中,有時候會出現bean之間的循環依賴,那什麼是循環依賴呢?就是A依賴B,B依賴A,在A未加載完成的狀況下,同時B開始了加載,這個時候又要注入A,這樣就會出現問題。bash
SPRING 對循環依賴的解決學習
spring對單例的bean有循環依賴的解決方案,可是目前對原型模式下的bean的循環依賴並無很好的解決方案。spring對單例bean 是採用提早在內存緩存中放置ObjectFactory來提早曝光bean,從而來解決循環依賴的問題的。ui
之前知道有這麼一回事,可是沒從源碼層面去剖析,因此似懂非懂,這樣不像是一位技術的master。 這裏我對spring源碼進行了簡化。代碼以下:this
AbstractBeanFactory doGetBean 方法spa
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 這個 getSingleton 方法裏面有 singletonFactories 內存map 用來存儲未加載好的bean 對應的ObjectFactory
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 處理FactoryBean
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
try {
//遞歸加載依賴的bean
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
if (mbd.isSingleton()) {
// 重載的getSingleton方法, 第二個參數是回調,在執行這個方法的時候 會執行createBean 方法,在這個方法裏面,ObjectFactory被提早曝光了。
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
// 處理FactoryBean
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
return (T) bean;
複製代碼
在以上這段代碼裏面,關鍵的註釋已經被給了出來。咱們看看這段代碼作了什麼事情。code
第一步: 先從緩存裏面拿單例bean,若是拿不出來,那麼再進行下面步驟的加載。若是拿的出來,那麼對它進行FactoryBean的處理。orm
第二步: 遞歸加載依賴的bean。 這個for循環被我簡化了不少,若是有興趣的朋友能夠看看原來的樣子,原來的樣子中有個拋異常的代碼仍是很是值得學習的。遞歸
第三步: 遞歸到底層,若是是單例bean,那就調用重載的getSingleton方法,這個方法的第二個參數是回調,在回調creatBean方法調用的doCreatBean中判斷這個bean是否仍是在建立狀態中,若是是,那就把ObjectFactory提早曝光。這個提早曝光的代碼層次有點深,能夠點進源碼看一下。
第一個getSingleton方法
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject =null;
//這個單例是否是還在被建立,若是是,那麼就去取singletonFactories 中的 ObjectFactory
if (isSingletonCurrentlyInCreation(beanName)) {
if (allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
}
}
}
return singletonObject;
}
複製代碼
看看以上代碼作了什麼:
這些代碼也被我簡化過,主要仍是從ObjectFactory池中拿ObjectFactory。
重載的getSingleton方法
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//這個方法很重要,比較關鍵
beforeSingletonCreation(beanName);
try {
//執行回調
singletonObject = singletonFactory.getObject();
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
複製代碼
第二個重載的方法比較重要一樣也被我簡化了,在beforeSingletonCreation 方法裏面,咱們在singletonsCurrentlyInCreation 這個set裏面加入了beanName,標誌這個bean正在被建立。那麼執行到回調方法singletonFactory.getObject()的時候就開始提早曝光ObjectFactory了。咱們能夠來看看。
回調時調用的createBean方法的doCreateBean方法中有以下代碼:
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//提早曝光 ObjectFactory
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
複製代碼
咱們能夠看到earlySingletonExposure 布爾類型中有isSingletonCurrentlyInCreation方法,這個方法恰好也是根據上一步來的,看是否這個bean正在建立中。那麼若是是的,就提早曝光ObjectFactory。
因此,這樣spring就解決了單例模式下的循環依賴。有興趣的朋友能夠看看spring的源碼。