Spring IOC 源碼解析(三),Bean 實例的建立

1. 實列化前的準備工做

第一步,prepareRefresh() ,方法刷新容器上下文信息,主要是設置它的開始時間、關閉狀態、活躍狀態。同時根據上下文環境初始化一些佔位符的值,而且進行校驗

第二步,obtainFreshBeanFactory() ,設置 BeanFactory 的 serializationId 後返回設計模式

第三步,prepareBeanFactory(beanFactory) ,作進一步的 BeanFactory 的準備工做,主要是爲其增長 BeanClassLoader、BeanExpressionResolver、PropertyEditorRegistrar、BeanPostProcessor(BeanPostProcessor,ApplicationListenerDetector)等操做,之因此有這一步是由於後續的 Bean 的實例化之後 DI 的相關操做等會依賴這些類提供的操做;能夠理解是爲其設置了一系列的靜態代理,在 Bean 的實例化的過程當中某些操做就須要經過這些代理去完成app

2. 調用 BeanFactoryProcessor 和註冊 BeanPostProcessor

BeanFactoryProcessor 涉及到的內容也比較多,會單獨寫一篇文章來介紹用法和分析源碼,這裏簡單介紹一下框架

首先調用 postProcessBeanFactory(beanFactory); 這是一個留給子類實現的方法,此處沒有作任何邏輯

而後調用 invokeBeanFactoryPostProcessors(beanFactory); 這裏註釋是調用已經註冊在上下文中的 BeanFactoryPostProcessor;實際上就算沒有註冊過的,它也會從 BeanFactory 中的 BeanDefinitionNames 中獲取對應的 BeanFactoryProcessor,而後調用 beanFactory.getBean 方法將其實例化。他主要作什麼工做呢,我舉個例子咱們使用 Spring 的時候有時候是基於 JavaConfig 的,會在某個類上面配置 @ComponentScan("com.xx.xxx"),這種在最初加載 BeanDefinition 的時候不會去解析 @ComponentScan("com.xx.xxx"),會在這個方法中解析該註解而且掃描對應路徑將其下的 BeanDefinition 轉入 BeanFactory 中。源碼分析

而後調用 registerBeanPostProcessors(beanFactory); 該方法會去 BeanFactory 中的 BeanDefinitions 中獲取 BeanPostProcessor 的實現類的 BeanDefinition 而後調用 getBean 方法將其實例化,而後將他們進行排序後註冊到 BeanFactory 的 beanPostProcessors 中。至關於就是提早一步將其實列化爲對象,以便於爲後續實列化對象這個過程服務。post

BeanPostProcessor 有不少的實現的子類,它們分別穿插於實例化 Bean 邏輯的各個地方,能夠極其靈活的調整實列化方式,和增長代理,修改 Bean 等功能;好比能夠在 Bean 實例化前檢查時候註冊有 InstantiationAwareBeanPostProcessor 該實現能夠提早返回代理對象,若是返回了,那麼後續的實例化操做就不會在進行;好比 MergedBeanDefinitionPostProcessor 他能夠在建立了早期 Bean 對象(後面會將上面是早期 Bean 對象)以後去合併 BeanDefinition 的內容,此次修改能夠影響到後續給早期 Bean 注入屬性值等內容等等設計

3. 實列化單例對象

實列化 Bean 的切入點主要邏輯就發生在這個方法中

3.1 獲取已經實例的對象

第一步, getSingleton 能夠從中獲取已經註冊到容器中的單例對象,或者早期單例對象;獲取到單例對象咱們很好理解,好比 A 依賴於 B,在對 A 進行依賴注入的時候,會遞歸的實列化 A 所依賴的對象好比 B,若是這裏 B 已經被實例化過了那麼直接返回就能附帶完整 A 的依賴注入。那麼早期對象是什麼意思呢,好比對象 C 依賴對象 D,對象 D 又依賴對象 C,目前正在進行 C 的實例化操做 D 還未被實例化,此時首先根據反射建立出 C 實例,而後將其放入早期 Bean 實例的 Map 容器中,而後進行屬性填充發現依賴 D,此時調用 getBean 對 D 進行實例化,依然將其放入早期 Bean 實例的 Map 容器中,又發現 D 又依賴 C,怎麼辦呢這個時候其實 C 是沒有實例化和初始化完成的,其實就是從中取出以前放入早期 Bean 實例的 Map 中的 C 對象引用,而後返回,這樣就能解決循環依賴問題

第二步, getObjectForBeanInstance 意思就是返回的 Bean 實例多是個 FactoryBean 也多是個普通 Bean 實例,那麼就封裝個方法普通 Bean 直接返回,FactoryBean 就調用其 getObject 方法來返回 Bean 實例(這裏和本文都只是描述主要內容,還有一些其它邏輯本身看源碼吧)3d

3.2 檢查 BeanDefinition 是否存在於當前 BeanFactory

第一步,獲取 ParentBeanFactory(能夠經過 setParent 設置父 BeanFactory 實現)

第二步,若是在當前 BeanFactory 中沒有找到該 BeanName 對應的 BeanDefinition 就遞歸的尋找父工廠,直到找到爲止代理

3.3 建立早期 Bean 實例

第一步,調用 resolveBeanClass 從 BeanDefinition 中獲取其 Class 屬性

第二步,調用 resolveBeforeInstantiation 給 BeanPostProcessor 一個返回代理對象的機會,若是返回了的話,就不在走後續的實列化,初始化等流程了,這個方法的核心邏輯以下 cdn

好比咱們自定義了一個實現類以下

此時對 c 這個類的實列化的後續流程將不在執行,此處直接返回該代理對象,而後會調用 applyBeanPostProcessorsAfterInitialization 直接初始化的後續邏輯
通常來講若是沒有特殊設置這裏是沒有返回代理對象的,將會繼續執行後續步驟

第一步,調用 createBeanInstance 建立 BeanWrapper,一個 Bean 的包裝類,此處點擊去看發現通常狀況下就是走的反射建立,構造器.newInstance(args) 來建立該對象而且對其進行包裝後返回

第二步,獲取到 BeanWrapper 中的 早期 Bean 實例,之因此說是早期是由於它仍是一個不完整的實列還未經歷初始化和屬性注入等相關操做對象

第三步,執行 MergedBeanDefinitionPostProcessor 的一些操做合併 BeanDefinition 的信息若是須要的話

這裏調用 addSingletonFactory 就是解決循環依賴問題的第二個關鍵步驟,分別將其添加到 singletonFactories 和 registeredSingletons 中,咱們在來配合這 doGetBean 中調用 getSingleton 方法來看

首先 singletonFactories 中獲取到 ObjectFactory 而後調用其 getObject 方法後放入 earlySingletonObjects 中而且返回,在調用 getObject 的時候實際上是調用的 getEarlyBeanReference(beanName, mbd, bean) 這個方法
可以看到這裏又有 BeanPostProcessor 的操做,主要作的邏輯就是在返回 Bean 實例前又留給用戶能夠調整早期 Bean 實例的機會

到這一步早期的 Bean 實例就建立完成了,後面就是該填充 Bean 的屬性和執行初始化方法了。目前能夠看到 BeanPostProcessor 穿插在實例實例化的各個地方,極盡量的給框架提供更增強大的拓展性,後面的邏輯還會再次接觸到 BeanPostProcessor

總結

早期 Bean 的實列化的流程,首先進行 BeanFactory 的準備工做包括設置 ClassLoader 設置基礎 BeanPostProcessor 初始化佔位符對應的值設置 BeanFactory 的工做狀態等待。而後調用 BeanFactoryPostProcessor 給容器一次再調整容器中的 BeanDefinitions 的機會(好比說將 @ComponentScan("com.xxx")路徑下的註解類掃描裝載爲 BeanDefinition 到容器中和調用咱們自定義的一些 BeanFactoryProcessor 等)。隨後根據 BeanDefinition 取出 Class 而後經過反射進行實例化一個早期的 Bean 實例,固然在以前還有一個調用 BeanPostProcessor 存在一個返回代理對象的機會若是返回就不進行後續的邏輯。最後將早期 Bean 實例構建爲 ObjectFactory 放入 singletonFactories 中結合 getSingleton 方法完美的解決了單例的循環依賴問題,因爲是一個 ObjectFactory 在調用 getObject 的時候會調用 getEarlyBeanReference 方法其中會檢測若是有 BeanPostProcessor(SmartInstantiationAwareBeanPostProcessor) 的話會調用該實現來覆蓋當前的早期 Bean 對象

根據目前幾章的源碼分析可以看到,Spring 利用了各類設計模式和鉤子方法和 BeanFactoryProcessor 和 BeanPostProcessor 使得其及其靈活,利用這些咱們可以很容易的定製第三方框架功能。目前運用了代理模式、委派模式、魔板方法模式、策略模式、工廠模式等等,後面會單獨寫一篇文章來聊聊 Spring 中的這些設計模式

相關文章
相關標籤/搜索