spring在DefaultSingletonBeanRegistry類中有以下幾種緩存池:
/** Cache of singleton objects: bean name to bean instance. */
//singletonObjects:key存bean name,value存bean實例
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** Set of registered singletons, containing the bean names in registration order. */
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
/** Names of beans that are currently in creation. */
//singletonsCurrentlyInCreation存正在創建的bean名稱
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/** Names of beans currently excluded from in creation checks. */
private final Set<String> inCreationCheckExclusions =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
類DefaultSingletonBeanRegistry:
/**
* Callback before singleton creation.
* 在單例bean創建之前嘗試回調
* <p>The default implementation register the singleton as currently in creation.
* 將單例bean註冊爲正在創建中的默認實現
* @param beanName the name of the singleton about to be created
* 參數爲將要創建的bean名稱
* @see #isSingletonCurrentlyInCreation
*/
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
比如當A、B兩個service相互依賴的時候:
@Service
public class ServiceAImpl implements ServiceA {
private ServiceB serviceB;
public ServiceAImpl(ServiceB serviceB) {
this.serviceB = serviceB;
}
public String print() {
return "service a print ";
}
}
@Service
public class ServiceBImpl implements ServiceB {
private final ServiceA serviceA;
public ServiceBImpl(ServiceA serviceA) {
this.serviceA = serviceA;
}
public String print() {
return serviceA.print() + "service b print ";
}
}
將工程啓動,當spring創建bean時候,比如首先創建A:
可以看到首先將beanname serviceAImpl放到了singletonsCurrentlyInCreation,而由於serviceAImpl依賴了B,這時spring嘗試初始化serviceBImpl,又將serviceBImpl放到了singletonsCurrentlyInCreation,又發現serviceBImpl依賴了serviceAImpl,因此又會嘗試初始化serviceAImpl;再次來到beforeSingletonCreation方法:
注意這裏Set singletonsCurrentlyInCreation裏面已經包括了serviceAImpl和serviceBImpl,再次執行將導致!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)兩個條件同時滿足,直接導致了拋出異常throw new BeanCurrentlyInCreationException(beanName);
看一下BeanCurrentlyInCreationException
提示Requested bean is currently in creation: Is there an unresolvable circular reference?
說明spring確實檢查到了循環依賴,並且沒有辦法處理,只能拋出異常
如果將其中一個bean改成使用setter注入方式:
@Service
public class ServiceAImpl implements ServiceA {
private ServiceB serviceB;
public String print() {
return " service a print ";
}
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
再次執行:
首先將A將入到singletonsCurrentlyInCreation,然後往下走,在getSingleton方法中,serviceAImpl添加到了到了earlySingletonObjects中,也就是所謂的提前暴露。
等到再次回到beforeSingletonCreation時,準備創建B,這時發現singletonsCurrentlyInCreation裏面已經空了,而我們在registeredSingletons裏面發現了ServiceAImpl的蹤影:
說明當用setter方法注入的時候實際上會用默認無參構造函數將A實例化,而A持有的對象B將延後初始化,等到單例B真正被初始化後,纔將B的實例設置到A中。
如果將B改成setter注入也是一樣的結果。
結論:
如果兩個bean之間有循環依賴,並且都是用構造器方式注入時,由於spring要使用默認構造函數實例化bean,會將兩個bean都放到待創建set後然後拋異常。
而只要有至少一個bean初始化方式爲setter方式時,就可以使用無參構造函數先將bean初始化,等到所依賴的bean被構造出來後再執行set,也就解決了循環依賴的問題。