Spring Bean創(chuàng)建和循環(huán)依賴
1 前言
前文已經(jīng)講述了Spring BeanFactory 與 FactoryBean 的區(qū)別詳情,在本文中將繼續(xù)講解 Bean 的創(chuàng)建和初始化,在這個(gè)環(huán)節(jié)中將會涉及到 Bean 的創(chuàng)建、初始化和循環(huán)依賴內(nèi)容。
2 Bean 的創(chuàng)建
在前文中已經(jīng)講述了 Spring 容器啟動的核心方法 refresh,關(guān)于 Bean 的創(chuàng)建和初始化方法都是在 finishBeanFactoryInitialization() 中進(jìn)行處理,在這個(gè)階段就是處理所有剩余的非懶加載的單例對象。

在該方法中,調(diào)用 preInstantiateSingletons() 進(jìn)行 Bean 的所有單實(shí)例 bean。 在這個(gè)過程中,會獲取容器中的所有 Bean, 依次進(jìn)行初始化和創(chuàng)建對象。獲取所有的 Bean 定義信息 beanDefinitionNames。在處理 Bean 時(shí)需要判斷 Bean 定義信息是不是抽象的,單例,和懶加載。其核心方法為 getBean , 也許大家都知道在獲取 Bean 的過程中,會經(jīng)歷 getBean -> doGetBean -> createBean -> doCreateBean 方法調(diào)用鏈,在 Spring 源碼中, doXXX 的方法都是實(shí)際業(yè)務(wù)的方法,在 doCreateBean 方法中,createBeanInstance 方法是真實(shí)創(chuàng)建 Bean 對象的方法,在 Spring 中,都是采用反射的方法來創(chuàng)建對象的。這些核心的方法都是在 AbstractAutowireCapableBeanFactory 中實(shí)現(xiàn),下圖便是 doCreateBean 方法,其中的核心操作有三個(gè): createBeanInstance 、populateBean、initializeBean。

createBeanInstance

createBeanInstance 是創(chuàng)建 Bean 對象的方法,這里最終調(diào)用的是 instantiateBean 方法,最終的調(diào)用棧如下:
AbstractAutowireCapableBeanFactory.instantiate
-> SimpleInstantiationStrategy.instantiate
-> BeanUtils.instantiateClass
-> ctor.newInstancepopulateBean
populateBean 是設(shè)置 Bean 屬性的方法,如下圖所示 autowireByName 和 autowireByType 兩個(gè)方法即是自動注入的方法,以 autowireByName 為例,獲取屬性是以 getBean 的方式從 IOC 容器中獲取對應(yīng)的 Bean。

initializeBean
初始化 Bean 是在實(shí)例化之后的操作,在初始化之前和之后便是 BeanPostProcessor 的操作,初始化的操作便是 invokeInitMethods 的初始化方法。
# 在初始化之前和之后執(zhí)行 applyBeanPostProcessorsBeforeInitialization applyBeanPostProcessorsAfterInitialization
初始化 Bean 的操作

初始化之前和之后的操作方法:

循環(huán)依賴問題
循環(huán)依賴是繞不開的話題,循環(huán)依賴的問題具體的表現(xiàn)形式如下:

在講循環(huán)依賴如何結(jié)果之前,還是涉及到 Bean 是如何創(chuàng)建的,如下圖所示的過程就是解決循環(huán)依賴的過程。

- 1 在創(chuàng)建 A 對象時(shí),需要在 populateBean 填充屬性時(shí)觸發(fā)獲取 B 對象的操作,這里說一下會在 createBeanInstance 方法中將對象的構(gòu)造方法放進(jìn)三級緩存中。
- 2 在經(jīng)歷了一輪 getBean 和 createBean 之后再次執(zhí)行到屬性賦值操作 populateBean,此時(shí)會再次觸發(fā)獲取 A 對象的操作,此時(shí)再去獲取 A 對象時(shí),會從三級緩存中創(chuàng)建一個(gè)半成品 A 對象放進(jìn)二級緩存中并刪除三級緩存,并做返回,此時(shí) B 對象得到屬性填充,完成賦值后放進(jìn)一級緩存中,并將 B 對象返回到 1 步驟。
- 3 第一步的創(chuàng)建 A 對象繼續(xù),完成屬性賦值后,會將對象放進(jìn)一級緩存中,并刪除二級緩存。 創(chuàng)建 Bean 的過程如下圖所示,
Abstra ctAutowireCapableBeanFactory.doCreateBean方法核心內(nèi)容如下:

獲取單例 Bean 的方法:

初始化的方法如下所示:

通過以上的三個(gè)步驟,就實(shí)現(xiàn)了循環(huán)依賴的問題解決,也完成了 Bean 對象的創(chuàng)建過程。

為什么要使用三級緩存呢,說到底是要解決以下問題:
- 1 如果采用了一級緩存,如果沒有存在循環(huán)依賴的問題,確實(shí)是可以的。如果有存在前圖中的循環(huán)依賴問題,那么就無法解決了,就只能采用兩級緩存才能解決了。
- 2 如果使用了兩級緩存,確實(shí)能解決一部分的問題。但是 Bean 被 AOP 代理,再使用兩級緩存就不能解決問題了,必須采用三級緩存。

總結(jié)
文中主要講述了 Spring 容器中 Bean 的創(chuàng)建過程已經(jīng)主要的方法,另外也著重分析了循環(huán)依賴的問題.
到此這篇關(guān)于Spring-Bean創(chuàng)建和循環(huán)依賴的文章就介紹到這了,更多相關(guān)Spring Bean 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IDEA之web項(xiàng)目導(dǎo)入jar包方式
這篇文章主要介紹了IDEA之web項(xiàng)目導(dǎo)入jar包方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05
java并發(fā)編程專題(二)----如何創(chuàng)建并運(yùn)行java線程
這篇文章主要介紹了java并發(fā)編程如何創(chuàng)建并運(yùn)行java線程,文中講解非常詳細(xì),示例代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-06-06
java并發(fā)包中CountDownLatch和線程池的使用詳解
這篇文章主要介紹了java并發(fā)包中CountDownLatch和線程池的使用詳解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02
mybatis-plus 擴(kuò)展批量新增的實(shí)現(xiàn)
本文主要介紹了mybatis-plus 擴(kuò)展批量新增的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01
Java基于socket實(shí)現(xiàn)簡易聊天室實(shí)例
這篇文章主要介紹了Java基于socket實(shí)現(xiàn)簡易聊天室的方法,實(shí)例分析了java基于socket實(shí)現(xiàn)聊天室服務(wù)端與客戶端的相關(guān)技巧,需要的朋友可以參考下2015-05-05
Java程序設(shè)計(jì)之12個(gè)經(jīng)典樣例
這篇文章主要給大家分享關(guān)于Java程序設(shè)計(jì)11個(gè)經(jīng)典樣例,主要以舉例的形式詳細(xì)的講解了Java程序設(shè)計(jì)的各種方法,需要的朋友可以參考一下文章具體的內(nèi)容2021-10-10
java中實(shí)現(xiàn)兼容ie6 7 8 9的spring4+websocket
這篇文章主要介紹了java中實(shí)現(xiàn)兼容ie6 7 8 9的spring4+websocket程序代碼,十分的簡單實(shí)用,有需要的小伙伴可以參考下。2015-06-06
Spring定時(shí)任務(wù)關(guān)于@EnableScheduling的用法解析
這篇文章主要介紹了Spring定時(shí)任務(wù)關(guān)于@EnableScheduling的用法,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06

