SpringIOC DI循環(huán)依賴實(shí)例詳解
要弄清楚循環(huán)依賴
1、需要知道Bean初始化的兩個(gè)階段
① Bean實(shí)例化創(chuàng)建實(shí)例對(duì)象(new Bean())
② Bean實(shí)例對(duì)象初始化(DI:注解自動(dòng)注入)
2、DefaultSingletonBeanRegistry類中的5個(gè)容器
/** 記錄已將創(chuàng)建的單例<beanName,singletonBean> */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** 記錄singletonFactory<beanName,singletonFactory> singeletonFactory中存放beanName和上面的①階段的bean:Bean實(shí)例化實(shí)例對(duì)象(還未初始化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)依賴實(shí)現(xiàn)
①bean初始化前后會(huì)打標(biāo),加入到singletonsCurrentlyInCreation容器中,這個(gè)打標(biāo)會(huì)在核心方法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表達(dá)式:其實(shí)是調(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)依賴的兩個(gè)打標(biāo)
//this.earlySingletonObjects.remove(beanName);
//this.registeredSingletons.add(beanName);放入單例beanName容器中
addSingleton(beanName, singletonObject);
...
}
}
② 上面singletonObject = singletonFactory.getObject()時(shí)向singletonFactories中記錄了(new Bean()),singletonFactories也會(huì)在核心方法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);
//將實(shí)例化bean(①階段)、beanName組裝成singletonFactory裝入singletonFactories容器
//this.earlySingletonObjects.remove(beanName);
//刪除earlySingletonObjects中beanName
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
...
//實(shí)例初始化 就是在這里面實(shí)現(xiàn)依賴注入DI的:反射實(shí)現(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)依賴核心就在于這個(gè)判斷,由于打標(biāo)+記錄了①階段的bean,
//循環(huán)依賴第二次調(diào)用getBean("a")時(shí),這里會(huì)直接返回第一次調(diào)用getBean("a")創(chuàng)建的①階段的bean
//而不會(huì)調(diào)用createBean("a")再次bean初始化(造成兩個(gè)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)建打標(biāo),①中beforeSingletonCreation()
//A實(shí)例化后保存到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))由于打標(biāo)了所以返回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é)
未看源碼之前,其實(shí)對(duì)循環(huán)依賴有一個(gè)想法:循環(huán)依賴可以看做是一個(gè)死鎖。
預(yù)防死鎖的方法:打破死鎖的四個(gè)必要條件(互斥、請(qǐng)求并等待、不可剝奪、循環(huán)等待),由于循環(huán)依賴的資源是對(duì)象自身,所以常用破壞循環(huán)等待條件方法:編號(hào)順序執(zhí)行,不適用
選擇破壞請(qǐng)求并等待條件:先創(chuàng)建對(duì)象,再賦值,模型
A a = new A();
B b = new B();
A.b = b;
B.a = a;
研究源碼之后發(fā)現(xiàn):想法差不多,但是代碼實(shí)現(xiàn)非常精彩。模型(打標(biāo)沒想到過)
A a = new A();
B b = new B();
b.a = a;
a.b = b;
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
spring boot里增加表單驗(yàn)證hibernate-validator并在freemarker模板里顯示錯(cuò)誤信息(推
這篇文章主要介紹了spring boot里增加表單驗(yàn)證hibernate-validator并在freemarker模板里顯示錯(cuò)誤信息的相關(guān)資料,需要的朋友可以參考下2018-01-01
SpringBoot實(shí)現(xiàn)動(dòng)態(tài)增刪啟停定時(shí)任務(wù)的方式
在spring?boot中,可以通過@EnableScheduling注解和@Scheduled注解實(shí)現(xiàn)定時(shí)任務(wù),也可以通過SchedulingConfigurer接口來實(shí)現(xiàn)定時(shí)任務(wù),但是這兩種方式不能動(dòng)態(tài)添加、刪除、啟動(dòng)、停止任務(wù),本文給大家介紹SpringBoot實(shí)現(xiàn)動(dòng)態(tài)增刪啟停定時(shí)任務(wù)的方式,感興趣的朋友一起看看吧2024-03-03
Java生產(chǎn)者消費(fèi)者的三種實(shí)現(xiàn)方式
這篇文章主要介紹了Java生產(chǎn)者消費(fèi)者的三種實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
Java?EasyExcel實(shí)現(xiàn)合并相同內(nèi)容單元格與動(dòng)態(tài)標(biāo)題功能
這篇文章主要為大家詳細(xì)介紹了Java?EasyExcel如何實(shí)現(xiàn)合并相同內(nèi)容單元格與動(dòng)態(tài)標(biāo)題功能,文中的示例代碼講解詳細(xì),有需要的小伙伴可以參考下2023-12-12
Java簡(jiǎn)單實(shí)現(xiàn)定時(shí)器
這篇文章主要為大家詳細(xì)介紹了Java簡(jiǎn)單實(shí)現(xiàn)定時(shí)器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-04-04
Hibernate實(shí)現(xiàn)many-to-many的映射關(guān)系
今天小編就為大家分享一篇關(guān)于Hibernate實(shí)現(xiàn)many-to-many的映射關(guān)系,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-03-03

