前言:
// 疑問點: 先進行 dependon 判斷
String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } registerDependentBean(dep, beanName); try { getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } // Create bean instance. if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }
這里先簡單記錄下 現在理解的 循環依賴的大致流程:
1、depende-on 標簽的情況
<bean id="aService" class="com.zzf.spring.dependent.AService" depends-on="bService"/> <bean id="bService" class="com.zzf.spring.dependent.BService" depends-on="aService"/>
注: depends-on適用於表面上看起來兩個bean之間沒有使用屬性之類的強連接的bean,但是兩個bean又確實存在前后依賴關系的情況,使用了depends-on的時候,依賴他人的bean是先於被依賴bean銷毀的。 一般不會這么使用。
也就是這樣配置的情況,才會拋出 BeanCreationException 異常。
if (isDependent(beanName, dep)) {// 判斷返回 true, 拋出循環依賴的exception throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); }
2、正確的 xml 配置的循環依賴 demo
<bean id="aService" class="com.zzf.spring.dependent.AService"> <property name="bService" ref="bService"/> </bean> <bean id="bService" class="com.zzf.spring.dependent.BService"> <property name="aService" ref="aService"/> </bean>
3、注解的方式解決循環依賴
@Service public class Aservice { @Autowired private BService service; } @Service public class BService { @Autowired private Aservice aservice; }
都說這段是 解決 循環依賴的 關鍵所在:
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; }
這里主要涉及到3個緩存, singletonObjects,earlySingletonObjects, singletonFactories。
- singletonObjects: 單例對象的 cache
- singletonFactories: 單例對象工廠的 cache
- earlySingletonObjects: 提前曝光的單例對象的 cache。(這是關鍵)
這里只考慮 A--B --A的情況:
Object sharedInstance = getSingleton(beanName);
第一次 getBean(A)的時候, 返回是 null, 會走如下流程:
// Create bean instance. if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }
在 createBean -> doCreate中有如下: addSingletonFactory() 提前 曝光當前類 工廠,到 singletonFactory中
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"); } addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
然后執行 protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) 進行 屬性的 賦值。 在進行 B的屬性賦值中, 發現B還沒有初始化, 則會去 調用 BeadFactory.getBean(B) 進行 B的初始化。
調用B的過程中,和A類似, 也會 提前 在singletonFactory 中曝光, 然后在 populateBean 中,注入 A屬性值時, 因為A未初始化,再次 去請求 getBean(A), 這次 在 getSingleton()中,因為 A提前曝光,所以在getSingleton 中 返回 A(可能未完全初始化),最終調用 getObjectForBeanInstance 方法,返回 完全實例話的 bean A, 然后注入到B 中,並完成B的 初始化, bean都會 放進singletonObjects 緩存中。
TODO: 在 populateBean 中怎么檢測 到 properties,這塊還需 仔細的去debug,還沒完全理清楚。
參考資料:http://cmsblogs.com/?p=2887,看了多次,總結的很好。