在容器啟動快完成時,會把所有的單例bean進行例項化,也可以叫做預先例項化。
這樣做的好處之一是,可以及早地發現問題,及早的丟擲異常,及早地解決掉。
本文就來看下整個的例項化過程。其實還是比較繁瑣的。
一、從容器中找出所有的bean定義名稱
因為不知道誰是單例bean,所以只能先全部找出來。如下圖01:
二、迴圈遍歷所有的bean名稱,檢查是否符合條件
首先要合併bean定義,因為bean定義可以有父子關係,類似繼承。
然後這個合併後的bean定義必須是,非抽象的,單例的,非延遲初始化的。
那麼它就滿足條件,如下圖02:
三、判斷是否為FactoryBean<?>型別
如果不是的話,說明該beanName對應一個普通的bean,可以直接例項化。
如果是的話,說明該beanName對應的是一個工廠,這個工廠本身是單例的。
但是它裡面生產的bean不一定是單例的。即使是的話,還要判斷是否要積極的去初始化工廠裡的bean。
具體的判斷如下圖03:
程式設計新說注:提到FactoryBean<?>型別,是否想起&符號的作用呢?
四、開始進入眾所熟知的getBean(String name)方法
在上一圖中可以看到Spring對bean的例項化時竟然是呼叫的getBean(..)方法。
這樣共用一套程式碼,簡單省事。不僅如此,當獲取一個bean的依賴時,也可以用該方法。
這樣getBean(..)就是一個綜合方法,沒有bean例項就生成,有的話就直接返回。
如下圖04:
五、對手工直接註冊的單例物件進行檢測
bean例項除了可以用bean定義生成外,還可以由開發人員直接註冊一個bean例項。
這樣在使用bean定義生成例項前,先使用beanName去手動註冊的bean例項集合中找一下。
如下圖05:
如果找到了,就不用生成了,否則就會根據bean定義生成bean例項。
六、對FactoryBean<?>型別的檢測
這和上面提到的是一個型別,它是一個工廠,可以認為是包裹在了實際bean例項的外面。
這樣可以有一些特殊的作用,不好之處就是每次都要檢測下,然後從它內部拿出實際的bean例項。
具體檢測過程不再展開,如下圖06:
七、對型別進行轉換,如果有必要的話
上面我們僅僅是用beanName去手動註冊的例項集合中尋找,萬一這個手動註冊的例項型別和bean定義要求的不相容呢?
因此要進行型別檢測與轉換,實在不行就拋異常,如下圖07:
如果成功的話,就表明手動註冊的bean定義例項滿足要求,將它返回即可。
程式設計新說注:如果在第五步沒有找到beanName對應的手動註冊的bean例項,那開始根據bean定義來生成bean例項。繼續往下看。
八、準備好顯式指定的依賴,如@DependsOn指定的
先獲取合併後的bean定義,然後從中讀出顯式指定的依賴,並逐個處理。
使用registerDependentBean(..)將依賴關係寫入容器,由容器維護。
並同樣使用getBean(..)方法例項化這些依賴,一模一樣的套路。
其實就是遞迴,如下圖08:
程式設計新說注:接下來使用createBean(..)方法正式開始建立bean
九、解析出bean的Class<?>
因為在註冊bean定義時並不一定載入類,可能只是一個字串的類名稱。
所以要根據類名稱去載入類,並得到類的Class<?>。如下圖09:
十、呼叫bean後處理器的postProcessBeforeInstantiation方法
此時還處在例項化之前,讓使用者有機會來提供一個bean例項或代理。
這樣Spring就不再進行後續的例項化步驟,直接返回這個使用者提供的。
如果使用者沒有提供的話,Spring繼續後續的處理。如下圖1011:
十一、呼叫InstanceSupplier生成bean例項,如果有的話
在註冊bean定義時,可以設定一個Supplier<?>型別的函式式介面。
其實就是使用者可以提供一段建立bean例項的程式碼,這樣Spring就使用它來建立bean例項。
然後將這個例項返回即可,如下圖12:
十二、通過FactoryMethod來生成bean例項,如果FactoryMethodName不為null的話
如下圖13:
FactoryMethod就是工廠方法,說明bean的例項是通過呼叫這個工廠方法返回的,而不是通過反射呼叫建構函式返回的。
工廠方法有兩種,靜態的和例項的。如果是例項的,那還要有一個FactoryBeanName來指定一個bean名稱,根據它可以從容器中獲取一個物件,用作工廠。
如果是靜態的,那就不需要例項了,直接把bean定義中的型別作為工廠類即可。如下圖14:
然後根據工廠方法的名稱,從bean定義中解析出對應的Method物件。然後再解析出構造方法引數用作工廠方法的引數。
最終通過反射呼叫這個工廠方法,獲取返回值,就是bean例項了,如下圖15:
這個bean例項會用一個BeanWrapper介面進行包裝,這個介面提供一些基礎的JavaBean功能,如資料的型別轉換然後再進行屬性繫結等。
十三、呼叫bean後處理器的determineCandidateConstructors方法來確定候選構造方法
如下圖16:
這裡涉及到從多個候選構造方法中選出一個最合適的,是一個比較複雜的過程。
最後也是通過反射呼叫構造方法,獲取到bean的例項。如下圖17:
然後也用BeanWrapper介面進行包裝。
十四、使用更適合的構造方法來例項化,如果有的話
如果上一步沒有執行的話,則使用bean定義中更適合的構造方法,如下圖18:
十五、使用預設無參的構造方法來例項化
如果上一步沒有執行的話,則使用預設無參構造方法,如下圖19:
程式設計新說注:至此bean例項已經建立好了。
十六、應用bean後處理器的postProcessMergedBeanDefinition方法
上兩篇文章詳細介紹了bean後處理器,主要是用來實現註解的功能的。
如下圖2021:
十七、此時就可以暴露早期的bean引用了,如果需要的話
如允許迴圈引用的話,就需要這個操作,如下圖22:
十八、應用bean後處理器的postProcessAfterInstantiation方法
如下圖23:
且該方法如果返回false,該bean例項後續的bean後處理器操作將不再執行。
十九、根據設定的自動裝配型別處理自動裝配問題
如下圖24:
如果配置的是按名稱自動裝配,則會把所有setter方法中引數型別是非基本型別的都找出來。
然後按照屬性名稱從容器中找出同名的bean,作為屬性值儲存起來以備後用。如下圖25:
如果配置的是按型別自動裝配,則會把所有setter方法中引數型別是非基本型別的都找出來。
然後按照屬性型別從容器中解析出對應的bean,作為屬性值儲存起來以備後用。如下圖26:
如果看了上兩篇文章,會發現這裡按型別從容器中解析bean的套路和上兩篇一模一樣。
程式設計新說注:
以上的setter方法上不需要標任何註解,因為顯式設定了自動裝配型別。
而預設情況其實是沒有設定的,即AUTOWIRE_NO,所以我們要標上@Autowired註解。
二十、應用bean後處理器的postProcessProperties方法
在這一步其實是完成了依賴的注入,如下圖27:
二十一、其餘屬性值到bean屬性的繫結
這一步是由BeanWrapper這個介面完成的,如下圖28:
程式設計新說注:至此bean的所有依賴裝配和屬性設定都已完畢。
二十二、應用bean後處理器的postProcessBeforeInitialization方法
這一步就開始執行初始化方法了。如下圖2930:
二十三、執行bean的初始化方法afterPropertiesSet()
如果bean實現了InitializingBean介面,此刻會呼叫它唯一的方法。
如下圖31:
二十四、執行bean定義中指定的初始化方法initMethod
如果bean定義是使用@Bean註冊的,可以通過設定註解屬性指定初始化方法。
如下圖32:
程式設計新說注:
之前文章中寫過,有三種方式可以指定初始化方法:
1)@PostConstruct註解,2)InitializingBean介面,3)@Bean註解
這裡有兩個問題需要記住:
1)如果兩種或三種方式都指向了同一個方法,這個方法也只會被執行一次。
2)三種方式指定的初始化方法的執行順序就按剛剛列出的1、2、3這個順序。
二十五、應用bean後處理器的postProcessAfterInitialization方法
如下圖3334:
程式設計新說注:至此bean的初始化工作已經完成。
二十六、註冊bean銷燬時要執行的程式碼,如果需要的話
除了使用了之前說過的三種方式指定過銷燬發方法之外,如果bean實現了AutoCloseable介面也算。
如果使用@Bean註冊且沒有指定銷燬方法,那麼預設把close和shutdown方法作為銷燬方法。
這些情況都是需要註冊的,如下圖35:
程式設計新說注:至此bean例項本身已經準好了。
二十七、快取單例的bean
如果這個bean是單例的,而且是新建立的,會把它快取到容器裡,以備後用。
如下圖36:
二十八、進行Scope處理
如果一個bean指定了Scope,即它的生命週期既非單例也非原型而是屬於某一個範圍。
Spring暫時支援的範圍如下圖37:
實現原理其實很簡單,比如Session範圍,那就先從Session中獲取,沒有的話生成一個放入Session中即可。
二十九、對FactoryBean<?>型別的檢測與處理
剛剛建立的這個bean可能是FactoryBean<?>型別,即一個工廠。而我們想要的可能是工廠裡生成的bean。
簡單來說,那就從工廠中把bean拿出來即可。
三十、型別的檢測與轉換
最終得到的bean例項可能與期望的型別不相容,此時就要進行型別轉換。
轉換成功的話就返回,失敗的話就丟擲型別不匹配異常。
至此一個bean的建立工作已經全部結束。
整體流程步驟就是這樣,只是忽略了一些和流程無關的細節實現。
原文:https://www.cnblogs.com/lixinjie/p/taste-spring-016.html