SpringIOC DI循環(huán)依賴實例詳解
要弄清楚循環(huán)依賴
1、需要知道Bean初始化的兩個階段
① Bean實例化創(chuàng)建實例對象(new Bean())
② Bean實例對象初始化(DI:注解自動注入)
2、DefaultSingletonBeanRegistry類中的5個容器
/** 記錄已將創(chuàng)建的單例<beanName,singletonBean> */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** 記錄singletonFactory<beanName,singletonFactory> singeletonFactory中存放beanName和上面的①階段的bean:Bean實例化實例對象(還未初始化DI)*/ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); /** 記錄早期的singletonBean 存放的也是① */ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); /** 存放已經(jīng)初始化后的beanName,有序 */ private final Set<String> registeredSingletons = new LinkedHashSet<>(256); /** 記錄正在初始化的bean的beanName */ private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
其中跟循環(huán)依賴相關(guān)的是singletonFactories、singeletonsCurrentlyInCreation、earlysingletonObjects.
3、循環(huán)依賴實現(xiàn)
①bean初始化前后會打標,加入到singletonsCurrentlyInCreation容器中,這個打標會在核心方法getSingleton()中起作用
/* org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>) */ public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { ... //循環(huán)依賴相關(guān):初始化前先singletonsCurrentlyInCreation.add(beanName) beforeSingletonCreation(beanName); ... //lamda表達式:其實是調(diào)用createBean(beanName, mbd, args):Bean初始化 singletonObject = singletonFactory.getObject(); ... //循環(huán)依賴相關(guān):初始化后singletonsCurrentlyInCreation.remove(beanName) afterSingletonCreation(beanName); ...//初始化完后 //this.singletonObjects.put(beanName, singletonObject);放入到單例容器中 //this.singletonFactories.remove(beanName);清空循環(huán)依賴的兩個打標 //this.earlySingletonObjects.remove(beanName); //this.registeredSingletons.add(beanName);放入單例beanName容器中 addSingleton(beanName, singletonObject); ... } }
② 上面singletonObject = singletonFactory.getObject()時向singletonFactories中記錄了(new Bean()),singletonFactories也會在核心方法getSingleton()中起作用
/* org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean */ protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { ... //循環(huán)依賴相關(guān)邏輯: //this.singletonFactories.put(beanName, singletonFactory); //將實例化bean(①階段)、beanName組裝成singletonFactory裝入singletonFactories容器 //this.earlySingletonObjects.remove(beanName); //刪除earlySingletonObjects中beanName addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); ... //實例初始化 就是在這里面實現(xiàn)依賴注入DI的:反射實現(xiàn) //調(diào)用AutowiredAnnotationBeanPostProcessor.postProcessProperties populateBean(beanName, mbd, instanceWrapper); ... }
③ 核心方法getSingleton
/* org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean) */ protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); //循環(huán)依賴核心就在于這個判斷,由于打標+記錄了①階段的bean, //循環(huán)依賴第二次調(diào)用getBean("a")時,這里會直接返回第一次調(diào)用getBean("a")創(chuàng)建的①階段的bean //而不會調(diào)用createBean("a")再次bean初始化(造成兩個bean的循環(huán)創(chuàng)建) 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; }
④ 循環(huán)依賴流程
/* org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean */ protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { ... //假設(shè)A、B互相依賴 //第一次getBean(A),sharedInstance == null,走else,createBean //A正在創(chuàng)建打標,①中beforeSingletonCreation() //A實例化后保存到singletonFactories中②中addSingletonFactory(beanName,singletonFactory) //DI依賴注入:②中populateBean(beanName,mbd,instanceWrapper),發(fā)現(xiàn)依賴B,調(diào)用getBean(B)初始化B的單例 //調(diào)用getBean(B)重復(fù)上面步驟,DI依賴注入發(fā)現(xiàn)依賴A,調(diào)用getBean(A) //第二次getBean(A),③中if(singletonObject == null && isSingletonCurrentlyInCreation(A))由于打標了所以返回singleFactory.getObject() //下面if條件直接返回bean,沒有走else破壞了循環(huán) Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { // bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { ... // createBean(beanName, mbd, args); bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } return bean; }
四、總結(jié)
未看源碼之前,其實對循環(huán)依賴有一個想法:循環(huán)依賴可以看做是一個死鎖。
預(yù)防死鎖的方法:打破死鎖的四個必要條件(互斥、請求并等待、不可剝奪、循環(huán)等待),由于循環(huán)依賴的資源是對象自身,所以常用破壞循環(huán)等待條件方法:編號順序執(zhí)行,不適用
選擇破壞請求并等待條件:先創(chuàng)建對象,再賦值,模型
A a = new A();
B b = new B();
A.b = b;
B.a = a;
研究源碼之后發(fā)現(xiàn):想法差不多,但是代碼實現(xiàn)非常精彩。模型(打標沒想到過)
A a = new A();
B b = new B();
b.a = a;
a.b = b;
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
java.Net.UnknownHostException異常處理問題解決
這篇文章主要介紹了java.Net.UnknownHostException異常處理方法,問題原因是在系統(tǒng)的?/etc/Hostname中配置了主機名,而在/etc/hosts文件中沒有相應(yīng)的配置,本文給大家詳細講解,需要的朋友可以參考下2023-03-03Lombok中@EqualsAndHashCode注解的使用及說明
這篇文章主要介紹了Lombok中@EqualsAndHashCode注解的使用及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03Java中HashMap和Hashtable的區(qū)別小結(jié)
本文主要介紹了Java中HashMap和Hashtable的區(qū)別小結(jié),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07關(guān)于springcloud報錯報UnsatisfiedDependencyException的問題
這篇文章主要介紹了關(guān)于springcloud報錯報UnsatisfiedDependencyException的問題,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11springmvc json類型轉(zhuǎn)換錯誤解決方案
這篇文章主要介紹了springmvc json類型轉(zhuǎn)換錯誤解決方案,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-12-12關(guān)于Process的waitFor死鎖問題及解決方案
這篇文章主要介紹了關(guān)于Process的waitFor死鎖問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12java加載properties文件的六種方法總結(jié)
這篇文章主要介紹了java加載properties文件的六種方法總結(jié)的相關(guān)資料,需要的朋友可以參考下2017-05-05