概述
回圈依賴就是依賴關系形成環,比如最簡單的回圈依賴:A物件依賴B,B物件依賴A
屬性注入與回圈依賴
- 如果是構造器注入,如果回圈依賴物件沒法構建,因為還未實體化
- 如果是屬性注入但是作用域是prototype,spring不會快取其物件實體,也不能處理回圈依賴的情況
- 如果是屬性注入singleton的,其bean的實體化程序與屬性注入程序是分開的,并且spring提供了三個map(就是大家說三級快取)來實作,
spring屬性注入處理回圈依賴的方式
通過以下xml方式配置一個回圈依賴的示例:
<bean id="person1" class="com.example.leetcode.spring.bean.Person">
<property name="parent" ref="person2"></property>
<property name="name" value="tom"></property>
</bean>
<bean id="person2" class="com.example.leetcode.spring.bean.Person">
<property name="parent" ref="person1"></property>
<property name="name" value="jack"></property>
</bean>
spring回圈依賴處理幾個關鍵位置:
- 獲取bean物件
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是否已經在注冊表,并回傳,
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
...
}
復制代碼
- DefaultSingletonBeanRegistry(單例物件注冊表)的幾個關鍵屬性,
// 用來存盤已經創建好的單例物件
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 用來存盤單例beanname到ObjectFactory的映射
/** 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);
- DefaultSingletonBeanRegistry.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;
}
- AbstractAutowireCapableBeanFactory.doCreateBean創建物件與注入屬性
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
...
instanceWrapper = createBeanInstance(beanName, mbd, args);
...
// 檢查是否提前將單例bean存入快取
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");
}
// 這里將beanname與工廠映射放入快取注冊表中(也就是上面的singletonFactories)
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
...
// 注入依賴屬性
populateBean(beanName, mbd, instanceWrapper);
...
}
假設我們從beanfactory獲取person1物件, 回圈依賴處理流程如下:
- 通過AbstractBeanFactory.doGetBean("persion1")獲取物件
- 因為一開始通過DefaultSingletonBeanRegistry.getSingleton()什么都沒有,進入AbstractAutowireCapableBeanFactory.doCreateBean()進行創建
- AutowireCapableBeanFactory.doCreateBean()里面執行完創建邏輯,因為是singleton將beanname與工廠的映射加入到addSingletonFactory()到快取
- 開始處理person1物件的屬性依賴populateBean()
- 當發現person1的parent屬性是一個參考時,通過beanfactory.getBean("person2")獲取依賴物件(org.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveReference)
- 此時進入person2的創建流程, person2也沒有快取,開始實體化并加入到addSingletonFactory()到快取
- person2在通過populateBean()注入屬性依賴發現依賴person1, 此時通過beanfactory.getBean("person1")獲取依賴物件
- 此時AbstractBeanFactory.doGetBean("persion1")獲取物件執行到getSingleton("person1")進行以下判斷:
- 從singletonObjects.get(beanName)獲取到null
- 進入if條件,對singletonObjects同步
- 從earlySingletonObjects.get(beanName);獲取也為null
- 進入內層if,通過singletonFactories.get(beanName);獲取到最開始bean實體化之后的beanname與工廠快取資訊
- 獲取到僅實體化完成的bean,并earlySingletonObjects.put(beanName, singletonObject);
- 然后洗掉singletonFactories.remove(beanName);
- 此時從getSingleton("person1")回傳了一個僅實體化尚未注入的bean參考
- person2在第7步獲取到person1僅實體化未注入的物件參考,
- person2完成屬性注入并回傳,
- person2被addSingleton(beanName, singletonObject);中singletonObjects.put(beanName, singletonObject)快取,并洗掉singletonFactories.remove(beanName);earlySingletonObjects.remove(beanName);
- person1在5步獲取到person2的物件并完成屬性注入
- person1物件回傳(因為一開始person2獲取的是person1的參考,此時person1完成注入是能看到注入后的物件)
- person1被addSingleton(beanName, singletonObject);中singletonObjects.put(beanName, singletonObject)快取,并洗掉singletonFactories.remove(beanName);earlySingletonObjects.remove(beanName);
- 回傳最終的person1物件
關于三個map(三級快取)
在出現回圈依賴時,三個map之間的流程如下:
- 先從singletonFactories獲取工廠,并通過getObject獲取物件并移除快取,將物件快取到earlySingletonObjects
- 通過earlySingletonObjects獲取提前曝光的物件
- 物件創建并初始化完成之后,物件資訊保留在singletonObjects并移除過earlySingletonObjects中的快取
earlySingletonObjects二級快取是雞肋嗎?
earlySingletonObjects快取的目的是,通過三級快取在獲取物件會執行一些列的后置處理器,通過earlySingletonObjects來快取提升性能,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/143785.html
標籤:其他
