javascript
从源码深处体验Spring核心技术--面试中IOC那些鲜为人知的细节
通過前面章節中對 Spring IOC 容器的源碼分析,我們已經基本上了解了 Spring IOC 容器對 Bean 定義資源的定位、載入和注冊過程,同時也清楚了當用戶通過 getBean()方法向 IOC 容器獲取被管理的 Bean時,IOC 容器對 Bean 進行的初始化和依賴注入過程,這些是 Spring IOC 容器的基本功能特性。
Spring IOC 容器還有一些高級特性,如使用 lazy-init 屬性對 Bean 預初始化、FactoryBean 產生或者修飾 Bean 對象的生成IOC 容器初始化 Bean 過程中使用 BeanPostProcessor 后置處理器對 Bean 聲明周期事件管理等。
關于延時加載
通過前面我們對 IOC 容器的實現和工作原理分析,我們已經知道 IOC 容器的初始化過程就是對 Bean定義資源的定位、載入和注冊,此時容器對 Bean 的依賴注入并沒有發生,依賴注入主要是在應用程序第一次向容器索取 Bean 時,通過 getBean()方法的調用完成。
當 Bean 定義資源的<Bean>元素中配置了 lazy-init=false 屬性時,容器將會在初始化的時候對所配置的 Bean 進行預實例化,Bean 的依賴注入在容器初始化的時候就已經完成。
這樣,當應用程序第一次向容器索取被管理的 Bean 時,就不用再初始化和對 Bean 進行依賴注入了,直接從容器中獲取已經完成依賴注入的現成 Bean,可以提高應用第一次向容器獲取 Bean 的性能。
1、refresh()方法
先從IOC 容器的初始化過程開始,我們知道 IOC 容器讀入已經定位的 Bean 定義資源是從 refresh()方法開始的,我們首先從 AbstractApplicationContext 類的 refresh()方法入手分析,源碼如下:
在 refresh()方法中
ConfigurableListableBeanFactorybeanFactory = obtainFreshBeanFactory();啟動了 Bean 定義資源的載入、注冊過程,而 finishBeanFactoryInitialization 方法是對注冊后的 Bean定義中的預實例化(lazy-init=false,Spring 默認就是預實例化,即為 true)的 Bean 進行處理的地方。
2、finishBeanFactoryInitialization 處理預實例化 Bean
當 Bean 定義資源被載入 IOC 容器之后,容器將 Bean 定義資源解析為容器內部的數據結構 BeanDefinition 注冊到容器中,AbstractApplicationContext 類中的 finishBeanFactoryInitialization()
方法對配置了預實例化屬性的 Bean 進行預初始化過程,源碼如下:
ConfigurableListableBeanFactory 是 一 個 接 口 , 其 preInstantiateSingletons() 方 法 由其子類DefaultListableBeanFactory 提供。
3、DefaultListableBeanFactory 對配置 lazy-init 屬性單態 Bean 的預實例化
通過對 lazy-init 處理源碼的分析,我們可以看出,如果設置了 lazy-init 屬性,則容器在完成 Bean 定義的注冊之后,會通過 getBean 方法,觸發對指定 Bean 的初始化和依賴注入過程,這樣當應用第一次向容器索取所需的 Bean 時,容器不再需要對 Bean 進行初始化和依賴注入,直接從已經完成實例化和依賴注入的 Bean 中取一個現成的 Bean,這樣就提高了第一次獲取 Bean 的性能。
關于 FactoryBean 和 BeanFactory
在 Spring 中,有兩個很容易混淆的類:BeanFactory?和?FactoryBean。
BeanFactory:Bean 工廠,是一個工廠(Factory),我們 Spring IOC 容器的最頂層接口就是這個BeanFactory,它的作用是管理 Bean,即實例化、定位、配置應用程序中的對象及建立這些對象間的依賴。
FactoryBean:工廠 Bean,是一個 Bean,作用是產生其他 bean 實例。通常情況下,這種 Bean 沒有什么特別的要求,僅需要提供一個工廠方法,該方法用來返回其他 Bean 實例。通常情況下,Bean 無須自己實現工廠模式,Spring 容器擔任工廠角色;但少數情況下,容器中的 Bean 本身就是工廠,其作用是產生其它 Bean 實例。
當用戶使用容器本身時,可以使用轉義字符”&”來得到 FactoryBean 本身,以區別通過 FactoryBean產生的實例對象和 FactoryBean 對象本身。在 BeanFactory 中通過如下代碼定義了該轉義字符:
String FACTORY_BEAN_PREFIX = "&";
如果 myJndiObject 是一個 FactoryBean,則使用&myJndiObject 得到的是 myJndiObject 對象,而不是 myJndiObject 產生出來的對象。
1、FactoryBean 源碼:
//工廠 Bean,用于產生其他對象 public interface FactoryBean<T> { //獲取容器管理的對象實例 @Nullable T getObject() throws Exception; //獲取 Bean 工廠創建的對象的類型 @Nullable Class<?> getObjectType(); //Bean 工廠創建的對象是否是單態模式,如果是單態模式,則整個容器中只有一個實例 //對象,每次請求都返回同一個實例對象 default boolean isSingleton() { return true; } }2、AbstractBeanFactory 的 getBean()方法調用 FactoryBean:
在前面我們分析 Spring IOC 容器實例化 Bean 并進行依賴注入過程的源碼時,提到在 getBean()方法觸發容器實例化 Bean 的時候會調用 AbstractBeanFactory 的 doGetBean()方法來進行實例化的過程,
源碼如下:
在 上 面 獲 取 給 定 Bean 的 實 例 對 象 的 getObjectForBeanInstance() 方 法 中 , 會 調 用 FactoryBeanRegistrySupport 類的 getObjectFromFactoryBean()方法,該方法實現了 Bean 工廠生產 Bean 實例對象。
Dereference(解引用):一個在 C/C++中應用比較多的術語,在 C++中,”*”是解引用符號,而”&”是引用符號,解引用是指變量指向的是所引用對象的本身數據,而不是引用對象的內存地址。
3、AbstractBeanFactory 生產 Bean 實例對象
AbstractBeanFactory 類中生產 Bean 實例對象的主要源碼如下:
從上面的源碼分析中,我們可以看出,BeanFactory 接口調用其實現類的 getObject 方法來實現創建Bean 實例對象的功能。
4、工廠 Bean 的實現類 getObject 方法創建 Bean 實例對象
FactoryBean 的實現類有非常多,比如:Proxy、RMI、JNDI、ServletContextFactoryBean 等等,FactoryBean 接口為 Spring 容器提供了一個很好的封裝機制,具體的 getObject()有不同的實現類根據不同的實現策略來具體提供,我們分析一個最簡單的 AnnotationTestFactoryBean 的實現源碼:
其他的 Proxy,RMI,JNDI 等等,都是根據相應的策略提供 getObject()的實現。這里不做一一分析,這已經不是 Spring 的核心功能,感興趣的小伙可以再去深入研究。
再述 autowiring
Spring IOC 容器提供了兩種管理 Bean 依賴關系的方式:
1)、顯式管理:通過 BeanDefinition 的屬性值和構造方法實現 Bean 依賴關系管理。
2)、autowiring:Spring IOC 容器的依賴自動裝配功能,不需要對 Bean 屬性的依賴關系做顯式的聲明,只需要在配置好 autowiring 屬性,IOC 容器會自動使用反射查找屬性的類型和名稱,然后基于屬性的類型或者名稱來自動匹配容器中管理的 Bean,從而自動地完成依賴注入。
通過對 autowiring 自動裝配特性的理解,我們知道容器對 Bean 的自動裝配發生在容器對 Bean 依賴注入的過程中。在前面對 Spring IOC 容器的依賴注入過程源碼分析中,我們已經知道了容器對 Bean 實例對象的屬性注入的處理發生在 AbstractAutoWireCapableBeanFactory 類中的 populateBean()方法中,我們通過程序流程分析 autowiring 的實現原理:
1、AbstractAutoWireCapableBeanFactory 對 Bean 實例進行屬性依賴注入
應用第一次通過 getBean()方法(配置了 lazy-init 預實例化屬性的除外)向 IOC 容器索取 Bean 時,容器創 建 Bean 實 例 對 象 , 并 且 對 Bean 實 例 對 象 進 行 屬 性 依 賴 注 入 , AbstractAutoWireCapableBeanFactory 的 populateBean()方法就是實現 Bean 屬性依賴注入的功能,其主要源碼如下:
2、Spring IOC 容器根據 Bean 名稱或者類型進行 autowiring 自動依賴注入
通過上面的源碼分析,我們可以看出來通過屬性名進行自動依賴注入的相對比通過屬性類型進行自動依賴 注 入 要 稍 微 簡 單 一 些 , 但 是 真 正 實 現 屬 性 注 入 的 是 DefaultSingletonBeanRegistry 類 的 registerDependentBean()方法。
3、DefaultSingletonBeanRegistry 的 registerDependentBean()方法對屬性注入
通過對 autowiring 的源碼分析,我們可以看出,autowiring 的實現過程:
a、對 Bean 的屬性代調用 getBean()方法,完成依賴 Bean 的初始化和依賴注入。
b、將依賴 Bean 的屬性引用設置到被依賴的 Bean 屬性上。
c、將依賴 Bean 的名稱和被依賴 Bean 的名稱存儲在 IOC 容器的集合中。
Spring IOC 容器的 autowiring 屬性自動依賴注入是一個很方便的特性,可以簡化開發時的配置,但是凡是都有兩面性,自動屬性依賴注入也有不足,首先,Bean 的依賴關系在 配置文件中無法很清楚地看出來,對于維護造成一定困難。其次,由于自動依賴注入是 Spring 容器自動執行的,容器是不會智能判斷的,如果配置不當,將會帶來無法預料的后果,所以自動依賴注入特性在使用時還是綜合考慮。
?
總結
以上是生活随笔為你收集整理的从源码深处体验Spring核心技术--面试中IOC那些鲜为人知的细节的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从源码深处体验Spring核心技术--基
- 下一篇: 双机热备架构