軟銀在日本推出送餐機器人,幫助緩解用工荒:每月費用 6461 元
概述
迴圈依賴就是依賴關係形成環,比如最簡單的迴圈依賴: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,bean)); } ... // 注入依賴屬性 populateBean(beanName,instanceWrapper); ... }
假設我們從beanfactory獲取person1物件, 迴圈依賴處理流程如下:
1.通過AbstractBeanFactory.doGetBean("persion1")獲取物件
2.因為一開始通過DefaultSingletonBeanRegistry.getSingleton()什麼都沒有,進入AbstractAutowireCapableBeanFactory.doCreateBean()進行建立
3.AutowireCapableBeanFactory.doCreateBean()裡面執行完建立邏輯,因為是singleton將beanname與工廠的對映加入到addSingletonFactory()到快取
4.開始處理person1物件的屬性依賴populateBean()
5.當發現person1的parent屬性是一個引用時,通過beanfactory.getBean("person2")獲取依賴物件(org.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveReference)
6.此時進入person2的建立流程, person2也沒有快取,開始例項化並加入到addSingletonFactory()到快取
7.person2在通過populateBean()注入屬性依賴發現依賴person1,此時通過beanfactory.getBean("person1")獲取依賴物件
8.此時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);
9.此時從getSingleton("person1")返回了一個僅例項化尚未注入的bean引用
10.person2在第7步獲取到person1僅例項化未注入的物件引用。
11.person2完成屬性注入並返回。
12.person2被addSingleton(beanName,singletonObject);中singletonObjects.put(beanName,singletonObject)快取,並刪除singletonFactories.remove(beanName);earlySingletonObjects.remove(beanName);
13.person1在5步獲取到person2的物件並完成屬性注入
14.person1物件返回(因為一開始person2獲取的是person1的引用,此時person1完成注入是能看到注入後的物件)
15.person1被addSingleton(beanName,singletonObject)快取,並刪除singletonFactories.remove(beanName);earlySingletonObjects.remove(beanName);
16.返回最終的person1物件
關於三個map(三級快取)
在出現迴圈依賴時,三個map之間的流程如下:
先從singletonFactories獲取工廠,並通過getObject獲取物件並移除快取,將物件快取到earlySingletonObjects
通過earlySingletonObjects獲取提前曝光的物件
物件建立並初始化完成之後,物件資訊保留在singletonObjects並移除過earlySingletonObjects中的快取
earlySingletonObjects二級快取是雞肋嗎?
earlySingletonObjects快取的目的是,通過三級快取在獲取物件會執行一些列的後置處理器,通過earlySingletonObjects來快取提升效能。
以上就是spring解決迴圈依賴的詳細內容,更多關於sping 迴圈依賴的資料請關注我們其它相關文章!