本文配合下面兩文食用味道更加spring
google上都有這樣的答案app
思考
想知道爲何spring爲何有這樣的問題 前提是知道spring是怎麼實例化Bean的函數
咱們知道Spring實例化(不管單例仍是多例)會先進入getBean 接着會進入doGetBean 它的源碼重點大體以下(不瞭解的話 請去看下Spring啓動過程——源碼分析2)源碼分析
//先從singletonObjects拿
//拿不到 調用beforeSingletonCreation this.singletonsCurrentlyInCreation.add(beanName)
從this.singletonFactories.get(beanName).getObject()
getSingleton(beanName);
//通過上面的仍是拿不到
//一樣先從singletonObjects拿 拿不到調用createBean 進入後會進到createBeanInstance
//createBeanInstance會根據實例化方式進行各自的實例化
sharedInstance = getSingleton(beanName, () -> createBean(beanName, mbd, args))
//建立完Bean放入緩存singletonFactories.put(beanName, singletonFactory)
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
!! 此時singletonObjects不存在上面的bean
//填充屬性
populateBean(beanName, mbd, instanceWrapper);
//初始化Bean 與這裏無關
exposedObject = initializeBean(beanName, exposedObject, mbd);
return exposedObject;
複製代碼
//這個方法不是很清楚,但我debug發現基本都是直接return bean
getEarlyBeanReference(beanName, mbd, bean)
複製代碼
重點getSingleton(beanName)
post
Bean依賴時執行流程圖 StudentA和StudentB相互依賴的狀況(從左到右Bean分別是StudentA、StudentB、StudentA)ui
Spring容器會將每個正在建立的Bean 標識符放在一個「當前建立Bean池」 Set集合singletonsCurrentlyInCreation中,Bean標識符在建立過程當中將一直保持 在這個池中,所以若是在建立Bean過程當中發現本身已經在「當前建立Bean池」裏時將拋出BeanCurrentlyInCreationException異常表示循環依賴this
三種注入方式
屬性注入
有參注入
setter注入google
根據上邊的流程圖 那麼三種注入方式下,實例化的過程有什麼不同呢? 首先回顧上面的內容(實在太繞了 再看一下)
spring單例下真正實例化Bean的地方
getSingleton(beanName, singletonFactory)
複製代碼
大體執行以下
Object singletonObject = this.singletonObjects.get(beanName);
if(singletonObject!=null) return;
//singletonsCurrentlyInCreation.add(name) 解決循環依賴
beforeSingletonCreation(beanName);
//真正實例化bean 其實也就是createBean這個方法 //最終會進入到無參或者有參的實例化
singletonObject = singletonFactory.getObject();
//singletonsCurrentlyInCreation.remove(name) 解決循環依賴
afterSingletonCreation(beanName);
addSingleton(beanName, singletonObject);//加入緩存
return singleObject
複製代碼
spring多例下真正實例化Bean的地方
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
//this.prototypesCurrentlyInCreation.set(beanName)
beforePrototypeCreation(beanName);
//最終會進入到無參或者有參的實例化
prototypeInstance = createBean(beanName, mbd, args)
//this.prototypesCurrentlyInCreation.remove();
afterPrototypeCreation(beanName)
複製代碼
public BeanCurrentlyInCreationException(String beanName) {
super(beanName,
"Requested bean is currently in creation: Is there an unresolvable circular reference?");
}
複製代碼
以StudentA->StudentB->StudentA->爲例
首先StudentA調用getBean進入createBeanInstance這個方法裏,因爲是無參實例化 會直接用反射返回一個對象bean,再調用 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))暴露ObjectFactory,接着進入populateBean,在這裏屬性仍是setter注入的區別就是經過不一樣的方式去對StudentB賦值,同樣的是都要去getBean("StudentB")->getSingleton("StudentB")->getSingleton("StudentB",()->createBean))->createBeanInstance->addSingletonFactory->populateBean->getSingleton("StudentA") 執行到這裏 會直接return 剛剛addSingletonFactory時緩存的Bean
此時singletonsCurrentlyInCreation也是有StudentA、B的 只不過沒有執行到getSingleton("StudentB",()->createBean)) 因此也就不會報異常
進入createBeanInstance後
有參構造函數實例化會進入autowireConstructor方法 StudentA會先去獲取它參數裏的StudentB 此時調用getBean->getSingleton("StudentB",()->createBean))->beforeSingletonCreation("StudentB")->createBeanInstance 再次進入autowireConstructor
StudentB會先去獲取它參數裏的StudentA 此時調用getBean->getSingleton->beforeSingletonCreation("StudentA")
因爲每次singletonsCurrentlyInCreation.add(beanName) 此時singletonsCurrentlyInCreation已經有StudentA
singletonsCurrentlyInCreation.add(beanName)==false -> 拋出BeanCurrentlyInCreationException
首先StudentA調用getBean進入doGetBean->isPrototypeCurrentlyInCreation("StudentA")->beforePrototypeCreation("StudentA")->createBean("StudentA", mbd, args)->createBeanInstance->populate->doGetBean("StudentB")->isPrototypeCurrentlyInCreation("StudentA")->beforePrototypeCreation("StudentB")->createBean("StudentB", mbd, args)->createBeanInstance->populate->doGetBean("StudentA")->isPrototypeCurrentlyInCreation("StudentA")此時返回true 直接拋異常
首先StudentA調用getBean進入doGetBean 多例直接進入->isPrototypeCurrentlyInCreation("StudentA")->beforePrototypeCreation("StudentA")->createBean("StudentA", mbd, args)->createBeanInstance->autowireConstructor->doGetBean->isPrototypeCurrentlyInCreation("StudentB")->beforePrototypeCreation("StudentB")->createBean("StudentB", mbd, args)->createBeanInstance->autowireConstructor->doGetBean->**isPrototypeCurrentlyInCreation("StudentA")**此時返回true 直接拋異常
和上邊差很少
Spring利用遞歸方式實現Bean的實例化過程,單例下利用singletonsCurrentlyInCreation判斷是不是循環依賴,多例下利用prototypesCurrentlyInCreation判斷, 開始建立bean會往集合add值,結束時則remove掉,這樣的規則,致使了多例下沒法解決循環依賴,單例下沒法解決構造參數注入的循環依賴
重點是理解整個建立過程