Spring Bean創(chuàng)建和循環(huán)依賴(lài)
1 前言
前文已經(jīng)講述了Spring BeanFactory 與 FactoryBean 的區(qū)別詳情,在本文中將繼續(xù)講解 Bean 的創(chuàng)建和初始化,在這個(gè)環(huán)節(jié)中將會(huì)涉及到 Bean 的創(chuàng)建、初始化和循環(huán)依賴(lài)內(nèi)容。
2 Bean 的創(chuàng)建
在前文中已經(jīng)講述了 Spring 容器啟動(dòng)的核心方法 refresh,關(guān)于 Bean 的創(chuàng)建和初始化方法都是在 finishBeanFactoryInitialization()
中進(jìn)行處理,在這個(gè)階段就是處理所有剩余的非懶加載的單例對(duì)象。
在該方法中,調(diào)用 preInstantiateSingletons()
進(jìn)行 Bean 的所有單實(shí)例 bean。 在這個(gè)過(guò)程中,會(huì)獲取容器中的所有 Bean, 依次進(jìn)行初始化和創(chuàng)建對(duì)象。獲取所有的 Bean 定義信息 beanDefinitionNames
。在處理 Bean 時(shí)需要判斷 Bean 定義信息是不是抽象的,單例,和懶加載。其核心方法為 getBean , 也許大家都知道在獲取 Bean 的過(guò)程中,會(huì)經(jīng)歷 getBean -> doGetBean -> createBean -> doCreateBean
方法調(diào)用鏈,在 Spring 源碼中, doXXX 的方法都是實(shí)際業(yè)務(wù)的方法,在 doCreateBean
方法中,createBeanInstance
方法是真實(shí)創(chuàng)建 Bean 對(duì)象的方法,在 Spring 中,都是采用反射的方法來(lái)創(chuàng)建對(duì)象的。這些核心的方法都是在 AbstractAutowireCapableBeanFactory
中實(shí)現(xiàn),下圖便是 doCreateBean 方法,其中的核心操作有三個(gè): createBeanInstance 、populateBean、initializeBean
。
createBeanInstance
createBeanInstance
是創(chuàng)建 Bean 對(duì)象的方法,這里最終調(diào)用的是 instantiateBean
方法,最終的調(diào)用棧如下:
AbstractAutowireCapableBeanFactory.instantiate -> SimpleInstantiationStrategy.instantiate -> BeanUtils.instantiateClass -> ctor.newInstance
populateBean
populateBean
是設(shè)置 Bean 屬性的方法,如下圖所示 autowireByName
和 autowireByType
兩個(gè)方法即是自動(dòng)注入的方法,以 autowireByName 為例,獲取屬性是以 getBean 的方式從 IOC 容器中獲取對(duì)應(yīng)的 Bean。
initializeBean
初始化 Bean 是在實(shí)例化之后的操作,在初始化之前和之后便是 BeanPostProcessor
的操作,初始化的操作便是 invokeInitMethods
的初始化方法。
# 在初始化之前和之后執(zhí)行 applyBeanPostProcessorsBeforeInitialization applyBeanPostProcessorsAfterInitialization
初始化 Bean 的操作
初始化之前和之后的操作方法:
循環(huán)依賴(lài)問(wèn)題
循環(huán)依賴(lài)是繞不開(kāi)的話(huà)題,循環(huán)依賴(lài)的問(wèn)題具體的表現(xiàn)形式如下:
在講循環(huán)依賴(lài)如何結(jié)果之前,還是涉及到 Bean 是如何創(chuàng)建的,如下圖所示的過(guò)程就是解決循環(huán)依賴(lài)的過(guò)程。
- 1 在創(chuàng)建 A 對(duì)象時(shí),需要在 populateBean 填充屬性時(shí)觸發(fā)獲取 B 對(duì)象的操作,這里說(shuō)一下會(huì)在 createBeanInstance 方法中將對(duì)象的構(gòu)造方法放進(jìn)三級(jí)緩存中。
- 2 在經(jīng)歷了一輪 getBean 和 createBean 之后再次執(zhí)行到屬性賦值操作 populateBean,此時(shí)會(huì)再次觸發(fā)獲取 A 對(duì)象的操作,此時(shí)再去獲取 A 對(duì)象時(shí),會(huì)從三級(jí)緩存中創(chuàng)建一個(gè)半成品 A 對(duì)象放進(jìn)二級(jí)緩存中并刪除三級(jí)緩存,并做返回,此時(shí) B 對(duì)象得到屬性填充,完成賦值后放進(jìn)一級(jí)緩存中,并將 B 對(duì)象返回到 1 步驟。
- 3 第一步的創(chuàng)建 A 對(duì)象繼續(xù),完成屬性賦值后,會(huì)將對(duì)象放進(jìn)一級(jí)緩存中,并刪除二級(jí)緩存。 創(chuàng)建 Bean 的過(guò)程如下圖所示,
Abstra ctAutowireCapableBeanFactory.doCreateBean
方法核心內(nèi)容如下:
獲取單例 Bean 的方法:
初始化的方法如下所示:
通過(guò)以上的三個(gè)步驟,就實(shí)現(xiàn)了循環(huán)依賴(lài)的問(wèn)題解決,也完成了 Bean 對(duì)象的創(chuàng)建過(guò)程。
為什么要使用三級(jí)緩存呢,說(shuō)到底是要解決以下問(wèn)題:
- 1 如果采用了一級(jí)緩存,如果沒(méi)有存在循環(huán)依賴(lài)的問(wèn)題,確實(shí)是可以的。如果有存在前圖中的循環(huán)依賴(lài)問(wèn)題,那么就無(wú)法解決了,就只能采用兩級(jí)緩存才能解決了。
- 2 如果使用了兩級(jí)緩存,確實(shí)能解決一部分的問(wèn)題。但是 Bean 被 AOP 代理,再使用兩級(jí)緩存就不能解決問(wèn)題了,必須采用三級(jí)緩存。
總結(jié)
文中主要講述了 Spring
容器中 Bean
的創(chuàng)建過(guò)程已經(jīng)主要的方法,另外也著重分析了循環(huán)依賴(lài)的問(wèn)題.
到此這篇關(guān)于Spring-Bean創(chuàng)建和循環(huán)依賴(lài)的文章就介紹到這了,更多相關(guān)Spring Bean 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決SpringCloud Config結(jié)合github無(wú)法讀取配置的問(wèn)題
這篇文章主要介紹了解決SpringCloud Config結(jié)合github無(wú)法讀取配置的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02Spring Security實(shí)現(xiàn)不同接口安全策略方法詳解
這篇文章主要介紹了Spring Security實(shí)現(xiàn)不同接口安全策略方法詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09MyBatis多對(duì)多關(guān)聯(lián)映射創(chuàng)建示例
這篇文章主要為大家介紹了MyBatis多對(duì)多關(guān)聯(lián)映射的創(chuàng)建示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06java連接sql server 2008數(shù)據(jù)庫(kù)代碼
Java的學(xué)習(xí),很重要的一點(diǎn)是對(duì)數(shù)據(jù)庫(kù)進(jìn)行操作。2013-03-03java打包成可執(zhí)行的jar或者exe的詳細(xì)步驟
Java程序完成以后,對(duì)于Windows操作系統(tǒng),習(xí)慣總是想雙擊某個(gè)exe文件就可以直接運(yùn)行程序,現(xiàn)我將一步一步的實(shí)現(xiàn)該過(guò)程.最終結(jié)果是:不用安裝JRE環(huán)境,不用安裝數(shù)據(jù)庫(kù),直接雙擊一個(gè)exe文件,就可以運(yùn)行程序2014-04-04SpringBoot統(tǒng)一返回處理出現(xiàn)cannot?be?cast?to?java.lang.String異常解決
這篇文章主要給大家介紹了關(guān)于SpringBoot統(tǒng)一返回處理出現(xiàn)cannot?be?cast?to?java.lang.String異常解決的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-09-09Java實(shí)現(xiàn)PDF文件的分割與加密功能
這篇文章主要為大家分享了如何利用Java語(yǔ)言實(shí)現(xiàn)PDF文件的分割與加密以及封面圖的生成,文中的示例代碼簡(jiǎn)潔易懂,感興趣的可以了解一下2022-04-04詳解spring cloud整合Swagger2構(gòu)建RESTful服務(wù)的APIs
這篇文章主要介紹了詳解spring cloud整合Swagger2構(gòu)建RESTful服務(wù)的APIs,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01IntelliJ IDEA下SpringBoot如何指定某一個(gè)配置文件啟動(dòng)項(xiàng)目
這篇文章主要介紹了IntelliJ IDEA下SpringBoot如何指定某一個(gè)配置文件啟動(dòng)項(xiàng)目問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09