在看面試題的時候忽然想到spring 是怎麼解決相互依賴的問題的?模糊不清,趕忙記錄下來。java
必定要將bean的實例化(beanFactory完成的)和完成bean的建立(ObjectFactory返回的)區分開,bean的實例化僅僅是得到了bean的實例,該bean仍在繼續建立之中,以後在該bean實例的基礎之上,還要作不少額外的操做,例如bean的屬性填充、處理器的應用、bean的循環依賴解決等,怎麼解決循環依賴的問題?面試
當ClassA引用ClassB,ClassB又引用ClassA,那麼兩個類之間就會造成一個閉環,致使循環依賴的出現。你們只需記住一點,Spring只能解決單例模式下的Setter循環依賴。spring
1.測試用例
bean和xml
package com.lyc.cn.v2.day01.cycle;緩存
/**
* @author: LiYanChao
* @create: 2018-10-16 23:59
*/
public class ClassA {
private ClassB classB;app
public ClassB getClassB() {
return classB;
}ide
public void setClassB(ClassB classB) {
this.classB = classB;
}
}
package com.lyc.cn.v2.day01.cycle;函數
/**
* @author: LiYanChao
* @create: 2018-10-16 23:59
*/
public class ClassB {
private ClassA classA;post
public ClassA getClassA() {
return classA;
}測試
public void setClassA(ClassA classA) {
this.classA = classA;
}
}
<!--循環依賴-->
<bean id="classA" class="com.lyc.cn.v2.day01.cycle.ClassA" scope="singleton">
<property name="classB" ref="classB"></property>
</bean>
<bean id="classB" class="com.lyc.cn.v2.day01.cycle.ClassB" scope="singleton">
<property name="classA" ref="classA"></property>
</bean>
結果
========測試方法開始=======this
com.lyc.cn.v2.day01.cycle.ClassB@2d6a9952
com.lyc.cn.v2.day01.cycle.ClassA@22a71081
========測試方法結束=======
當scope="singleton"時結果是正常的,Spring爲咱們解決了bean之間的循環依賴,再將scope改成prototype,運行測試用例(摘取部分異常信息):
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'classA': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:255)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:198)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:372)
... 40 more
從異常信息中能夠看到Is there an unresolvable circular reference?,有循環依賴異常,這也證實了Spring是不能解決prototype做用域的bean之間的循環依賴的。
下面咱們從源碼角度去分析,Spring是如何解決bean之間的循環依賴問題的。
引
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
// Instantiate the bean.
// ① 實例化bean
BeanWrapper instanceWrapper = null;
// 注意factoryBeanInstanceCache是ConcurrentMap,remove方法會返回刪除的鍵值(若是不存在返回null)
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
// 若是factoryBeanInstanceCache沒有緩存對應的BeanWrapper,則從新建立bean實例
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// Allow post-processors to modify the merged bean definition.
// ② 容許MergedBeanDefinitionPostProcessor後處理器修改已合併的bean定義。
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// ③ 提早緩存ObjectFactory以解決bean之間的循環依賴
// mbd.isSingleton()->是否單例,Spring只解決單例bean的循環依賴問題
// allowCircularReferences->是否容許循環依賴
// isSingletonCurrentlyInCreation->該bean是否建立中
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
// ④ 初始化bean實例 這裏你們要與第①步區分開,到這裏bean已經完成了實例化,可是尚未完成初始化的操做,例如bean的屬性填充
Object exposedObject = bean;
try {
// 填充bean屬性
populateBean(beanName, mbd, instanceWrapper);
// 初始化bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
// ⑤ 循環依賴檢查
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// Register bean as disposable.
try {
// ⑥ 根據bean的做用域註冊bean
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
// ⑦ 返回bean實例
return exposedObject;
}
經過第一步已經得到了bean的實例(第二步留在之後再講解),直接看第三步:提早緩存ObjectFactory以解決bean之間的循環依賴。
1.提早曝光對象
這裏涉及到一個很是重要的接口ObjectFactory,該接口是一個函數式接口且只有一個方法:T getObject() throws BeansException;,該方法用於返回一個bean的實例,此時的bean已經完成初始化,可是還沒有完成建立。
若是當前的bean知足條件,則將當前正在建立的bean和其ObjectFactory對象提早曝光,加入到正在建立bean池中。
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
2.循環依賴的解決
在完成bean的實例建立以後,還要填充bean的屬性,針對ClassA,其屬性是ClassB,若是要填充ClassA的屬性則勢必先要實例化ClassB,那麼這裏又涉及到一個概念,RuntimeBeanReference–>運行時引用。
打開BeanDefinitionValueResolver類的resolveValueIfNecessary方法。摘取代碼片斷(該方法會在之後所有分析)
判斷RuntimeBeanReference屬性
// ① RuntimeBeanReference->運行時引用
// 例如BeanA依賴BeanB,那麼在配置文件中有經過配置ref標籤進行引用的,在解析BeanDefinition的時候,是不會直接實例化BeanB的,那麼這個引用就是RuntimeBeanReference
if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
return resolveReference(argName, ref);
}
解析RuntimeBeanReference(運行時引用)
private Object resolveReference(Object argName, RuntimeBeanReference ref) {
try {
// 一、解析引用beanName
Object bean;
String refName = ref.getBeanName();
refName = String.valueOf(doEvaluate(refName));
// 二、判斷引用bean是否屬於父BeanFactory
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");
}
bean = this.beanFactory.getParentBeanFactory().getBean(refName);
}
// 三、從當前beanFactory獲取引用beanName實例
else {
bean = this.beanFactory.getBean(refName);
this.beanFactory.registerDependentBean(refName, this.beanName);
}
if (bean instanceof NullBean) {
bean = null;
}
return bean;
}
catch (BeansException ex) {
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
}
}
該過程很簡單,首先解析refBeanName,而後經過getBean方法獲取其實例,此時當前建立的bean是ClassA,引用bean是ClassB。
獲取到ClassB實例以後,又要填充ClassB的屬性,此時又會出現對RuntimeBeanReference的解析,即ClassA,再去獲取ClassA的實例,此時的ClassA的實例已經被提早曝光,會從緩存中獲取ClassA的實例。
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 一、從緩存中獲取bean
Object singletonObject = this.singletonObjects.get(beanName);
// 二、未能獲取到bean,可是容許對當前建立的單例的早期引用(解決循環引用)
// isSingletonCurrentlyInCreation-->判斷指定的單例bean是否當前正在建立(Spring只解決單例bean的循環依賴問題)
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 從earlySingletonObjects獲取提早曝光的bean
singletonObject = this.earlySingletonObjects.get(beanName);
// 未能獲取到提早曝光的bean且當前的bean容許被建立早期依賴
if (singletonObject == null && allowEarlyReference) {
// 從緩存中獲取BeanFactory
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 經過getObject()方法獲取提早曝光的bean
singletonObject = singletonFactory.getObject();
// 將獲取到的singletonObject緩存至earlySingletonObjects
this.earlySingletonObjects.put(beanName, singletonObject);
// 從singletonFactories移除bean
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
從singletonObjects中沒法獲取到bean的實例,由於此時bean還沒有完成所有建立,可是因爲咱們提早曝光了ObjectFactory,因此經過singletonObject = singletonFactory.getObject();是能夠獲取到bean的實例的。這樣就解決了Spring的循環依賴問題。
三.總結
一、Spring只能解決Setter方法注入的【單例】bean之間的循環依賴
二、ClassA依賴ClassB,ClassB又依賴ClassA,造成依賴閉環。Spring在獲取ClassA的實例時,發現ClassA在1級和2級緩存中不存在,會先在3級緩存中優先添加(曝光) 此時ClassA並未完成建立,就將其曝光加入正在建立的bean緩存中。
在解析ClassA的屬性時,又發現依賴於ClassB,再次去獲取ClassB(建立B的過程與A一致),當解析ClassB的屬性時,又發現須要ClassA的屬性,但此時的ClassA已經被提早曝光加入了正在建立的bean的緩存中(spring的3級緩存,其實就是3個map),則無需建立新的的ClassA的實例,直接從緩存中獲取便可,完成B對象中A屬性的注入流程,進而整個B的對象建立完成,return 給A一個B對象,A再進行注入並完成建立。從而解決循環依賴問題。
三、spring 的3級緩存
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
/** Cache of early singleton objects: bean name --> bean instance */ private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);