spring循環(huán)依賴策略解析
循環(huán)依賴
所謂循環(huán)依賴就是多個Bean之間依賴關(guān)系形成一個閉環(huán),例如A->B->C->...->A 這種情況,當(dāng)然,最簡單的循環(huán)依賴就是2個Bean之間互相依賴:A->B(A依賴B), B->A(B依賴A) 。在Spring中,如果A->B,那么在創(chuàng)建A的過程中會去創(chuàng)建B,在創(chuàng)建B(或B的依賴)的過程中又發(fā)現(xiàn)B->A,這個時候就出現(xiàn)了循環(huán)依賴的現(xiàn)象。
循環(huán)依賴的解決
spring中的循環(huán)依賴只有當(dāng)
1.Bean是單例,
2.通過屬性注入的情況
這兩個條件滿足的情況下是沒問題的。但是如果是通過構(gòu)造器依賴,或者不是單例模式的情況下循環(huán)依賴就會拋出異常BeanCurrentlyInCreationException。下面從代碼層面上解析一下為什么。
Prototype的循環(huán)依賴問題
為什么最先介紹Prototype的循環(huán)依賴呢,因為可以順便介紹在Spring中創(chuàng)建Bean的流程核心流程:在AbstractFoctory的doGetBean的方法。這個方法很長,這里只寫出核心邏輯,并在注解上注明了個人理解:
protected <T> T doGetBean( final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name); Object bean; //嘗試獲取單例對象,因為spring大部分的bean都是單例的,所以這里先嘗試能否獲取。 registered singletons. Object sharedInstance = getSingleton(beanName); //單例存在的情況下,那么beanName返回的肯定是單例類,但是這里還需要判斷是不是FactoryBean if (sharedInstance != null && args == null) { ... //FactoryBean應(yīng)該返回getObject()對象 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { //走到這里,有可能beanName是單例模式,但之前并沒有實例化,或者是Prototype類型。 //首先判斷不是循環(huán)依賴,這里的循環(huán)依賴指的是Prototype類型 if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } try { final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); // 如果是單例,則創(chuàng)建單例模式 if (mbd.isSingleton()) { // !!!這里是解決單例循環(huán)依賴的關(guān)鍵,后面再分析 sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { throw ex; } } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { // 原型模式,則創(chuàng)建一個新對象. Object prototypeInstance = null; try { /*這里是Prototype循環(huán)依賴的問題,會記錄在map中beanName, 如果在解決當(dāng)前Bean的依賴過程中還依賴當(dāng)前Bean, 則說明了出現(xiàn)了循環(huán)依賴 */ beforePrototypeCreation(beanName); prototypeInstance = createBean(beanName, mbd, args); } finally { //對應(yīng)beforePrototypeCreation(),從map中移除 afterPrototypeCreation(beanName); } bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } ... } } ... return (T) bean; }
可以看出,該流程中就考慮了Prototype的循環(huán)依賴的問題,只要在創(chuàng)建Prototype的Bean中出現(xiàn)循環(huán)依賴那么就拋出異常。但是在singleton的情況下,則通過另外的方式來解決。
Singleton的循環(huán)依賴之構(gòu)造注入
在上面介紹中,出現(xiàn)了一個很關(guān)鍵的地方:
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { throw ex; } } });
這個getSingleton涉及到了ObjectFactory這個接口類,這個接口的功能和FactoryBean類似,但是主要是用來解決循環(huán)依賴的。在初始化過程同決定返回的Singleton對象是。關(guān)于單例的對象的創(chuàng)建,又要介紹一下DefaultSingletonBeanRegistry這個類,這個類主要用來幫助創(chuàng)建單例模式,其中主要的屬性:
/** 緩存創(chuàng)建的單例對象: bean名字 --> bean對象 */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256); /** 緩存單例的factory,就是ObjectFactory這個東西,: bean name --> ObjectFactory */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16); /** 也是緩存創(chuàng)建的單例對象,功能和singletonObjects不一樣, 在bean構(gòu)造成功之后,屬性初始化之前會把對象放入到這里, 主要是用于解決屬性注入的循環(huán)引用: bean name --> bean instance */ private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16); /** 記錄在創(chuàng)建單例對象中循環(huán)依賴的問題,還記得Prototype中又記錄創(chuàng)建過程中依賴的map嗎? 在Prototype中只要出現(xiàn)了循環(huán)依賴就拋出異常,而在單例中會嘗試解決 */ private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));
現(xiàn)在回過來看getSingleton(beanName, new ObjectFactory<Object>()這個方法的實現(xiàn)。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { synchronized (this.singletonObjects) { //嘗試在singletonObjects中獲取 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { //不存在則創(chuàng)建 //把當(dāng)前beanName加入到singletonsCurrentlyInCreation中 beforeSingletonCreation(beanName); try { singletonObject = singletonFactory.getObject(); } ... finally { ... //從singletonsCurrentlyInCreation中刪除beanName afterSingletonCreation(beanName); } } return (singletonObject != NULL_OBJECT ? singletonObject : null); } }
這段邏輯是不是和Prototype中解決循環(huán)類似,這里其實就是調(diào)用了ObjectFactory的getObject()獲取對象,回過頭去看前面代碼,ObjectFactory的getObject()方法實際調(diào)用的是createBean(beanName, mbd, args)。說到createBean(beanName, mbd, args)又不得不說AbstractAutowireCapableBeanFactory這個類,主要功能就是完成依賴注入的Bean的創(chuàng)建,這個類的createBean方法代碼如下,注意注解說明:
@Override protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException { ... Object beanInstance = doCreateBean(beanName, mbdToUse, args); ... } protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException { // 實例化bean BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { //如果沒實例化則創(chuàng)建新的BeanWrapper //如果是通過構(gòu)造器注入,這里是一個關(guān)鍵點 /* 因為在A初始化的時候發(fā)現(xiàn)構(gòu)造函數(shù)依賴B,就會去實例化B, 然后B也會運行到這段邏輯,構(gòu)造函數(shù)中發(fā)現(xiàn)依賴A, 這個時候就會拋出循環(huán)依賴的異常 */ instanceWrapper = createBeanInstance(beanName, mbd, args); } //如果當(dāng)前是單例,并且allowCircularReferences為true(默認就是true,除非我們不希望Spring幫我們解決) boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { /* !??!這里很重要,把構(gòu)造成功,但屬性還沒注入的 的bean加到singletonFactory中,這樣再解決A的依賴 過程中如果依賴A,就把這個半成品返回回去。 */ addSingletonFactory(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean); } }); } Object exposedObject = bean; try { //自動注入屬性 populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { exposedObject = initializeBean(beanName, exposedObject, mbd); } } ... return exposedObject; }
注解已經(jīng)注明了我的理解。就不再贅述
總結(jié)
上面代碼是我一邊debug一個寫下的,現(xiàn)在寫完了,根據(jù)自己的理解總結(jié)一下。
相關(guān)類說明
AbstractBeanFactory,這個類中包含了Bean創(chuàng)建的主要流程,在doGetBean這個方法中包含了對Prototype循環(huán)依賴處理。邏輯很簡單,出現(xiàn)了循環(huán)依賴則直接拋出異常
DefaultSingletonBeanRegister 用于管理Singleton的對象的創(chuàng)建,以及解決循環(huán)依賴的問題,其中解決循環(huán)依賴的關(guān)鍵屬性就是了earlySingletonObjects,他會在構(gòu)造Singleton對象過程中暫時緩存構(gòu)造成功,但屬性還未注入的對象,這樣就可以解決循環(huán)依賴的問題。
AbstractAutowireCapableBeanFactory,自動注入的相關(guān)邏輯,包自動注入的對象的創(chuàng)建、初始化和注入。但如果在調(diào)用構(gòu)造函數(shù)中發(fā)現(xiàn)了循環(huán)依賴,則拋出異常
ObjectFactory,這個接口功能和FactoryBean類似,但是為了解決循環(huán)依賴,他決定了在獲取的getSingleton()是一個完成品還是一個半成品。
思考
如果A--構(gòu)造依賴->B,B--屬性依賴-->A,例如:
@Component public class BeanA { private BeanB beanB; @Autowired public BeanA(BeanB beanB) { this.beanB = beanB; } } @Component public class BeanB { @Autowired private BeanA beanA; }
這種情況會異常嗎?提示:都有可能
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringMVC數(shù)據(jù)頁響應(yīng)ModelAndView實現(xiàn)頁面跳轉(zhuǎn)
本文主要介紹了SpringMVC數(shù)據(jù)頁響應(yīng)ModelAndView實現(xiàn)頁面跳轉(zhuǎn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07java設(shè)計模式之實現(xiàn)對象池模式示例分享
對象池模式經(jīng)常用在頻繁創(chuàng)建、銷毀對象(并且對象創(chuàng)建、銷毀開銷很大)的場景,比如數(shù)據(jù)庫連接池、線程池、任務(wù)隊列池等。本代碼簡單,沒有限制對象池大小2014-02-02Spring Cloud Hystrix線程池不足的解決方法
這篇文章主要介紹了Spring Cloud Hystrix線程池不足的解決方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02springboot?實現(xiàn)動態(tài)刷新配置的詳細過程
這篇文章主要介紹了springboot實現(xiàn)動態(tài)刷新配置,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-05-05詳談java中int和Integer的區(qū)別及自動裝箱和自動拆箱
這篇文章主要介紹了詳談java中int和Integer的區(qū)別及自動裝箱和自動拆箱,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08