spring 如何检测到循环依赖/如何解决循环依赖
spring針對(duì)循環(huán)依賴問(wèn)題 不能完全解決 對(duì)于不能解決的只能檢測(cè)到并拋出異常
1. spring針對(duì)構(gòu)造器方法的 單實(shí)例對(duì)象和原型對(duì)象是無(wú)法解決循環(huán)依賴問(wèn)題的
先說(shuō)結(jié)論,
針對(duì)單例對(duì)象 getSingleton方法中 有個(gè)beforeSingletonCreation 方法 這個(gè)方法是用來(lái)檢測(cè)循環(huán)依賴的
原型對(duì)象isPrototypeCurrentlyInCreation方法beforePrototypeCreation方法配合檢測(cè)循環(huán)依賴
注:inCreationCheckExclusions和singletonsCurrentlyInCreation 是兩個(gè)set
1 protected void beforeSingletonCreation(String beanName) {
2 if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
3 throw new BeanCurrentlyInCreationException(beanName);
4 }
5 }
以AB兩個(gè)單例對(duì)象舉例,
1 Class A(){
2 A(B b){
3 }
4 }
5
6 Class B(){
7 B(A a){
8 }
9 }
對(duì)象A在實(shí)例化的時(shí)候(getBean方法),會(huì)先執(zhí)行beforeSingletonCreation方法 吧自己的beanName放入set中,然后去執(zhí)行實(shí)例化 解析構(gòu)造器方法的時(shí)候發(fā)現(xiàn) 需要用到對(duì)象B ,所以去getBean(B)
B對(duì)象在一級(jí)緩存是沒(méi)有的(因?yàn)槭沁€未實(shí)例化),所以去創(chuàng)建單例B,會(huì)執(zhí)行和A一樣的操作,把自己的beanName放入set中,然后解析構(gòu)造器的時(shí)候發(fā)現(xiàn)依賴A對(duì)象,去一級(jí)緩存獲取是沒(méi)有的(因?yàn)锳對(duì)象實(shí)例化還未完成 未放入到一級(jí)緩存中)
所以去實(shí)例化A 放入set的時(shí)候 發(fā)現(xiàn)已經(jīng)存在及會(huì)拋出異常
1 protected boolean isPrototypeCurrentlyInCreation(String beanName) {
2 Object curVal = this.prototypesCurrentlyInCreation.get();
3 return (curVal != null &&
4 (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
5 }
1 protected void beforePrototypeCreation(String beanName) {
2 Object curVal = this.prototypesCurrentlyInCreation.get();
3 if (curVal == null) {
4 this.prototypesCurrentlyInCreation.set(beanName);
5 }
6 else if (curVal instanceof String) {
7 Set<String> beanNameSet = new HashSet<>(2);
8 beanNameSet.add((String) curVal);
9 beanNameSet.add(beanName);
10 this.prototypesCurrentlyInCreation.set(beanNameSet);
11 }
12 else {
13 Set<String> beanNameSet = (Set<String>) curVal;
14 beanNameSet.add(beanName);
15 }
16 }
對(duì)象在實(shí)例化之前會(huì)先調(diào)用isPrototypeCurrentlyInCreation方法,如果set中沒(méi)有繼續(xù)執(zhí)行,實(shí)例化的時(shí)候調(diào)用beforePrototypeCreation方法 放入set中,在之后的循環(huán)依賴中 查詢到set中有自己的beanName 則拋出異常
那么spring對(duì)set方法注入 是如何解決循環(huán)依賴問(wèn)題的呢,一樣先說(shuō)結(jié)論 是使用第三級(jí)緩存來(lái)解決的
1. 在實(shí)例化bean A的時(shí)候 先去查看一級(jí)緩存中是否有(執(zhí)行的方法getSingleton)
并且會(huì)查詢currentlyCreationSet中有沒(méi)有
2. 處理一下depend-on依賴 或者檢查一下是否是抽象等等安全性校驗(yàn)
3.把單實(shí)例的A 的beanName放入currentlyCreationSet中
4. 實(shí)例化A對(duì)象因?yàn)槭莝et注入 所以這時(shí)使用的空參構(gòu)造 去反射出實(shí)例對(duì)象 得到一個(gè)早期對(duì)象(未進(jìn)行屬性注入的對(duì)象,未執(zhí)行init方法調(diào)用,未進(jìn)行后處理器處理,早期對(duì)象和處理后的對(duì)象的內(nèi)存地址是一樣的aop對(duì)象除外)
5. 早期實(shí)例A封裝到objectFactory對(duì)象中,放入三級(jí)緩存
6. 進(jìn)行依賴注入 依賴注入的時(shí)候發(fā)現(xiàn)依賴Bean B 再去執(zhí)行g(shù)etBean方法 重復(fù)執(zhí)行1-6步 拿到一個(gè)B的早期對(duì)象objectFactory 放入到三級(jí)緩存
7. 處理B的依賴注入 發(fā)現(xiàn)B依賴了A 執(zhí)行g(shù)etbean方法 從1開(kāi)始執(zhí)行 去一級(jí)緩存查詢沒(méi)有 去currentlyCreationSet查詢有A 去三級(jí)緩存中拿到A的早期實(shí)例 放入二級(jí)緩存清除三級(jí)緩存中的A早期實(shí)例并返回這個(gè)早期實(shí)例 B繼續(xù)執(zhí)行完成了B對(duì)象的實(shí)例 放入一級(jí)緩存中 清除掉二三級(jí)緩存相關(guān)數(shù)據(jù)
8. 繼續(xù)執(zhí)行A的實(shí)例化步驟 A完成實(shí)例化 存入一級(jí)緩存 清除二三級(jí)緩存相關(guān)數(shù)據(jù)
1 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
2 Object singletonObject = this.singletonObjects.get(beanName);
3 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
4 synchronized (this.singletonObjects) {
5 singletonObject = this.earlySingletonObjects.get(beanName);
6 if (singletonObject == null && allowEarlyReference) {
7 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
8 if (singletonFactory != null) {
9 singletonObject = singletonFactory.getObject();
10 this.earlySingletonObjects.put(beanName, singletonObject);
11 this.singletonFactories.remove(beanName);
12 }
13 }
14 }
15 }
16 return singletonObject;
17 }
總結(jié)
以上是生活随笔為你收集整理的spring 如何检测到循环依赖/如何解决循环依赖的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Linux学习之CentOS(二十九)-
- 下一篇: mysql快速导入5000万条数据过程记