在容器啟動快完成時,會把所有的單例bean進行實例化,也可以叫做預(yù)先實例化。
成都創(chuàng)新互聯(lián)公司專注于企業(yè)成都全網(wǎng)營銷、網(wǎng)站重做改版、任丘網(wǎng)站定制設(shè)計、自適應(yīng)品牌網(wǎng)站建設(shè)、H5網(wǎng)站設(shè)計、購物商城網(wǎng)站建設(shè)、集團公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站制作、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計等建站業(yè)務(wù),價格優(yōu)惠性價比高,為任丘等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。這樣做的好處之一是,可以及早地發(fā)現(xiàn)問題,及早的拋出異常,及早地解決掉。
本文就來看下整個的實例化過程。其實還是比較繁瑣的。
一、從容器中找出所有的bean定義名稱
因為不知道誰是單例bean,所以只能先全部找出來。如下圖01:
二、循環(huán)遍歷所有的bean名稱,檢查是否符合條件
首先要合并bean定義,因為bean定義可以有父子關(guān)系,類似繼承。
然后這個合并后的bean定義必須是,非抽象的,單例的,非延遲初始化的。
那么它就滿足條件,如下圖02:
三、判斷是否為FactoryBean>類型
如果不是的話,說明該beanName對應(yīng)一個普通的bean,可以直接實例化。
如果是的話,說明該beanName對應(yīng)的是一個工廠,這個工廠本身是單例的。
但是它里面生產(chǎn)的bean不一定是單例的。即使是的話,還要判斷是否要積極的去初始化工廠里的bean。
具體的判斷如下圖03:
注:提到FactoryBean>類型,是否想起&符號的作用呢?
四、開始進入眾所熟知的getBean(String name)方法
在上一圖中可以看到Spring對bean的實例化時竟然是調(diào)用的getBean(..)方法。
這樣共用一套代碼,簡單省事。不僅如此,當(dāng)獲取一個bean的依賴時,也可以用該方法。
這樣getBean(..)就是一個綜合方法,沒有bean實例就生成,有的話就直接返回。
如下圖04:
五、對手工直接注冊的單例對象進行檢測
bean實例除了可以用bean定義生成外,還可以由開發(fā)人員直接注冊一個bean實例。
這樣在使用bean定義生成實例前,先使用beanName去手動注冊的bean實例集合中找一下。
如下圖05:
如果找到了,就不用生成了,否則就會根據(jù)bean定義生成bean實例。
六、對FactoryBean>類型的檢測
這和上面提到的是一個類型,它是一個工廠,可以認(rèn)為是包裹在了實際bean實例的外面。
這樣可以有一些特殊的作用,不好之處就是每次都要檢測下,然后從它內(nèi)部拿出實際的bean實例。
具體檢測過程不再展開,如下圖06:
七、對類型進行轉(zhuǎn)換,如果有必要的話
上面我們僅僅是用beanName去手動注冊的實例集合中尋找,萬一這個手動注冊的實例類型和bean定義要求的不兼容呢?
因此要進行類型檢測與轉(zhuǎn)換,實在不行就拋異常,如下圖07:
如果成功的話,就表明手動注冊的bean定義實例滿足要求,將它返回即可。
注:如果在第五步?jīng)]有找到beanName對應(yīng)的手動注冊的bean實例,那開始根據(jù)bean定義來生成bean實例。繼續(xù)往下看。
八、準(zhǔn)備好顯式指定的依賴,如@DependsOn指定的
先獲取合并后的bean定義,然后從中讀出顯式指定的依賴,并逐個處理。
使用registerDependentBean(..)將依賴關(guān)系寫入容器,由容器維護。
并同樣使用getBean(..)方法實例化這些依賴,一模一樣的套路。
其實就是遞歸,如下圖08:
注:接下來使用createBean(..)方法正式開始創(chuàng)建bean
九、解析出bean的Class>
因為在注冊bean定義時并不一定加載類,可能只是一個字符串的類名稱。
所以要根據(jù)類名稱去加載類,并得到類的Class>。如下圖09:
十、調(diào)用bean后處理器的postProcessBeforeInstantiation方法
此時還處在實例化之前,讓用戶有機會來提供一個bean實例或代理。
這樣Spring就不再進行后續(xù)的實例化步驟,直接返回這個用戶提供的。
如果用戶沒有提供的話,Spring繼續(xù)后續(xù)的處理。如下圖1011:
十一、調(diào)用InstanceSupplier生成bean實例,如果有的話
在注冊bean定義時,可以設(shè)置一個Supplier>類型的函數(shù)式接口。
其實就是用戶可以提供一段創(chuàng)建bean實例的代碼,這樣Spring就使用它來創(chuàng)建bean實例。
然后將這個實例返回即可,如下圖12:
十二、通過FactoryMethod來生成bean實例,如果FactoryMethodName不為null的話
如下圖13:
FactoryMethod就是工廠方法,說明bean的實例是通過調(diào)用這個工廠方法返回的,而不是通過反射調(diào)用構(gòu)造函數(shù)返回的。
工廠方法有兩種,靜態(tài)的和實例的。如果是實例的,那還要有一個FactoryBeanName來指定一個bean名稱,根據(jù)它可以從容器中獲取一個對象,用作工廠。
如果是靜態(tài)的,那就不需要實例了,直接把bean定義中的類型作為工廠類即可。如下圖14:
然后根據(jù)工廠方法的名稱,從bean定義中解析出對應(yīng)的Method對象。然后再解析出構(gòu)造方法參數(shù)用作工廠方法的參數(shù)。
最終通過反射調(diào)用這個工廠方法,獲取返回值,就是bean實例了,如下圖15:
這個bean實例會用一個BeanWrapper接口進行包裝,這個接口提供一些基礎(chǔ)的JavaBean功能,如數(shù)據(jù)的類型轉(zhuǎn)換然后再進行屬性綁定等。
十三、調(diào)用bean后處理器的determineCandidateConstructors方法來確定候選構(gòu)造方法
如下圖16:
這里涉及到從多個候選構(gòu)造方法中選出一個最合適的,是一個比較復(fù)雜的過程。
最后也是通過反射調(diào)用構(gòu)造方法,獲取到bean的實例。如下圖17:
然后也用BeanWrapper接口進行包裝。
十四、使用更適合的構(gòu)造方法來實例化,如果有的話
如果上一步?jīng)]有執(zhí)行的話,則使用bean定義中更適合的構(gòu)造方法,如下圖18:
十五、使用默認(rèn)無參的構(gòu)造方法來實例化
如果上一步?jīng)]有執(zhí)行的話,則使用默認(rèn)無參構(gòu)造方法,如下圖19:
注:至此bean實例已經(jīng)創(chuàng)建好了。
十六、應(yīng)用bean后處理器的postProcessMergedBeanDefinition方法
上兩篇文章詳細(xì)介紹了bean后處理器,主要是用來實現(xiàn)注解的功能的。
如下圖2021:
十七、此時就可以暴露早期的bean引用了,如果需要的話
如允許循環(huán)引用的話,就需要這個操作,如下圖22:
十八、應(yīng)用bean后處理器的postProcessAfterInstantiation方法
如下圖23:
且該方法如果返回false,該bean實例后續(xù)的bean后處理器操作將不再執(zhí)行。
十九、根據(jù)設(shè)置的自動裝配類型處理自動裝配問題
如下圖24:
如果配置的是按名稱自動裝配,則會把所有setter方法中參數(shù)類型是非基本類型的都找出來。
然后按照屬性名稱從容器中找出同名的bean,作為屬性值保存起來以備后用。如下圖25:
如果配置的是按類型自動裝配,則會把所有setter方法中參數(shù)類型是非基本類型的都找出來。
然后按照屬性類型從容器中解析出對應(yīng)的bean,作為屬性值保存起來以備后用。如下圖26:
注:以上的setter方法上不需要標(biāo)任何注解,因為顯式設(shè)置了自動裝配類型。
而默認(rèn)情況其實是沒有設(shè)置的,即AUTOWIRE_NO,所以我們要標(biāo)上@Autowired注解。
二十、應(yīng)用bean后處理器的postProcessProperties方法
在這一步其實是完成了依賴的注入,如下圖27:
二十一、其余屬性值到bean屬性的綁定
這一步是由BeanWrapper這個接口完成的,如下圖28:
注:至此bean的所有依賴裝配和屬性設(shè)置都已完畢。
二十二、應(yīng)用bean后處理器的postProcessBeforeInitialization方法
這一步就開始執(zhí)行初始化方法了。如下圖2930:
二十三、執(zhí)行bean的初始化方法afterPropertiesSet()
如果bean實現(xiàn)了InitializingBean接口,此刻會調(diào)用它唯一的方法。
如下圖31:
二十四、執(zhí)行bean定義中指定的初始化方法initMethod
如果bean定義是使用@Bean注冊的,可以通過設(shè)置注解屬性指定初始化方法。
如下圖32:
注:三種可以指定初始化的方法:
1)@PostConstruct注解,
2)InitializingBean接口,
3)@Bean注解
這里有兩個問題需要記?。?/p>
1)如果兩種或三種方式都指向了同一個方法,這個方法也只會被執(zhí)行一次。
2)三種方式指定的初始化方法的執(zhí)行順序就按剛剛列出的1、2、3這個順序。
二十五、應(yīng)用bean后處理器的postProcessAfterInitialization方法
如下圖33、34:
注:至此bean的初始化工作已經(jīng)完成。
二十六、注冊bean銷毀時要執(zhí)行的代碼,如果需要的話
除了使用了之前說過的三種方式指定過銷毀發(fā)方法之外,如果bean實現(xiàn)了AutoCloseable接口也算。
如果使用@Bean注冊且沒有指定銷毀方法,那么默認(rèn)把close和shutdown方法作為銷毀方法。
這些情況都是需要注冊的,如下圖35:
注:至此bean實例本身已經(jīng)準(zhǔn)好了。
二十七、緩存單例的bean
如果這個bean是單例的,而且是新創(chuàng)建的,會把它緩存到容器里,以備后用。
如下圖36:
二十八、進行Scope處理
如果一個bean指定了Scope,即它的生命周期既非單例也非原型而是屬于某一個范圍。
Spring暫時支持的范圍如下圖37:
實現(xiàn)原理其實很簡單,比如Session范圍,那就先從Session中獲取,沒有的話生成一個放入Session中即可。
二十九、對FactoryBean>類型的檢測與處理
剛剛創(chuàng)建的這個bean可能是FactoryBean>類型,即一個工廠。而我們想要的可能是工廠里生成的bean。
簡單來說,那就從工廠中把bean拿出來即可。
三十、類型的檢測與轉(zhuǎn)換
最終得到的bean實例可能與期望的類型不兼容,此時就要進行類型轉(zhuǎn)換。
轉(zhuǎn)換成功的話就返回,失敗的話就拋出類型不匹配異常。
至此一個bean的創(chuàng)建工作已經(jīng)全部結(jié)束。
整體流程步驟就是這樣,只是忽略了一些和流程無關(guān)的細(xì)節(jié)實現(xiàn)。
點擊獲取?附送學(xué)習(xí)進階架構(gòu)資料、PDF書籍文檔、面試資料
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。