Spring的依賴注入容器

依賴注入是Spring框架最核心的能力,Spring框架提供的AOP,WebMVC等其它功能都是以依賴注入容器作為基礎構建的,Spring依賴注入容器類似于一個用于組裝對象的框架內核,任何應用或者基于Spring的框架都能從中受益

核心概念

理解一個項目,我習慣從它的領域模型開始,領域模型即項目中的核心概念,項目的一切都是圍繞其核心概念展開的,Spring的依賴注入容器中有哪些核心概念?


bean是spring中的核心概念之一,是spring操作的實體,對于bean,spring中有BeanDefinition接口與之對應,BeanDefinition接口表示bean的定義,它有多個實現(xiàn)類,比如GenericBeanDefinition、AnnotatedGenericBeanDefinition等,每個不同的實現(xiàn)類針對特定的配置方式

BeanFactory是spring的服務域,用于管理bean,BeanFactory也有多個實現(xiàn)類和抽象類,用于實現(xiàn)依賴注入容器的不同的使用方式

ApplicationContext是用于對BeanFactory做增強,提供了BeanFactory的生命周期管理,狀態(tài)變更廣播,注解配置等功能

核心類之間的關系

在介紹Spring的框架設計原則之前,我們先通過一個類圖來看一下Spring依賴注入容器的核心類之間的關系:

這里是Spring ApplicationContext的繼承結構中的幾個有代表性的類,由于相差類較多,這里省略了繼承關系中間的抽象類,有興趣的讀者可以自己翻閱Spring的源代碼,看看其實現(xiàn)細節(jié)。ApplicationContext的繼承體系使用了模板方法模式,Spring中不同的抽象類為不同類型的ApplicationContext提供了基本的支持,比如AbstractXmlApplicationContext是通過xml配置使用的ApplicationContext的抽象實現(xiàn)。抽象類中實現(xiàn)了ApplicationContext應該具備的共同特性和邏輯相同的方法,具體類中再實現(xiàn)具體的不同點的邏輯,當然,子類中可以覆蓋那些抽象類中的方法,以便對ApplicationContext做擴展。

我們先簡單的看一下ClassPathXmlApplicationContext類,很多初學者最初就是從ClassPathXmlApplicationContext類開始接觸Spring的ApplicationContext對象的,這類是通過加載classpath下的xml配置來初始化Spring的依賴注入容器,并完成bean的裝載和依賴對象的注入過程,此類的使用步驟如下:

[if !supportLists]1.???????[endif]添加Spring的配置文件,比如/META-INF/service.xml

[if !supportLists]2.???????[endif]在service.xml中配置需要的bean

[if !supportLists]3.???????[endif]通過service.xml創(chuàng)建ClassPathApplicationContext對象:

前面講述了常用的ClassPathXmlApplicationContext,現(xiàn)在這里再看一下一個不常用,卻更能說明ApplicationContext的結構的類GenericApplicationContext,從上面的繼承關系圖中可以看出此類實現(xiàn)了BeanDefinitionRegistry接口,BeanDefinitionRegistry接口有什么作用呢?我們在使用Spring的依賴注入容器前,都需要通過XML或者注解的方式向Spring中配置bean,Spring在解析xml或者注解的同時,會將每一個<bean>配置解析成一個BeanDefinition對象注冊到spring容器中,BeanDefinitionRegistry接口就是用來做bean的注冊的,可以先看一下BeanDefinitionRegistry接口的定義:

可以看到,BeanDefinitionRegistry接口提供了Bean的注冊相關的方法,可以用此接口向Spring中注冊BeanDefinition對象,移除BeanDefinition對象,獲取已注冊的BeanDefinition對象等,GenericApplicationContext類沒有配置加載功能,無法通過xml配置完成初始化,如果想要使用GenericApplicationContext類,則需要以編碼的方式向Spring中注冊Bean,例如:

我們再回到前文講到的ClassPathXmlApplicationContext,ClassPathXmlApplicationContext是GenericApplicationContext的子類,只不過ClassPathXmlApplicationContext通過解析xml的方式來完成對BeanDefinition的注冊前面講到的示例中,在xml中配的<bean name="userService" class="cn.yxffcode.springtest.service.UserServiceImpl"/>配置就類似于以繁瑣的編碼的方式使用GenericApplicationContext時通過代碼創(chuàng)建的BeanDefinition對象,如果我們打開ClassPathXmlApplicationContext及其父類AbstractXmlApplicationContext類,會發(fā)現(xiàn)它們有如下結構 (為了簡單起見,這里同樣省略了中間類,XmlBeanDefinitionReader實際上在AbstractXmlApplicationContext中):

使用ClassPathXmlApplicationContext后,注冊Bean的過程由GenericApplicationContext的手動編碼變成了的通過xml配置,注冊Bean的過程由XmlBeanDefinitionReader負責,配置只是編碼方式的一種簡化,它并不是框架的核心XmlBeanDefinitionReader的執(zhí)行過程后文會有詳細講解

框架的初始化

? ? ? ?上一節(jié)講過的GenericApplicationContext的使用中,調用了一次refresh方法完成了初始化,同樣,ClassPathXmlApplicationContext也需要通過refresh方法完成初始化,只不過默認是自動調用了refresh方法,如果打開ClassPathXmlApplicationContext的源碼,我們可以看到多個構造器,其中有一個構造器可以指定是否自動刷新依賴注入容器完成依賴注入窗口的初始化,如果想要手動初始化,我們可以換使用如下方式創(chuàng)建ClassPathXmlApplicationContext對象:

通過IDE的幫助,我們很容易能找到refresh方法的聲明,它聲明在ApplicationContext的一個子接口ConfigurabeApplicationContext中,我們暫且先不解析此接口的作用,我們可以先看一看refresh方法的實現(xiàn)。

? ? ? ?打開AbstractApplicationContext類的refresh方法,這是ApplicationContext的初始化入口,這里先介紹refresh方法的結構:

1. 首先被調用的是prepareRefresh方法,此方法是AbstractApplicationContext的模板方法模式的擴展方法,子類可覆蓋此方法,用于在初始化前做前置準備

2. 其次再是獲取ConfigurableListableBeanFactory對象,ApplicationContext并不是從頭開始做依賴注入容器,它是建立在BeanFactory之上,BeanFactory是Spring實現(xiàn)依賴注入的最核心的接口

3. 第三個步驟是調用prepareBeanFactory,此方法用于向Spring中注冊默認的單例bean以及默認的BeanPostProcessor等

4. 第四步是調用postProcessBeanFactory,此方法用于執(zhí)行準備好了BeanFactory的環(huán)境后的后置邏輯,子類可覆蓋它實現(xiàn)特定邏輯

5. 第五步是invokeBeanFactoryPostProcessors,從方法名上可以看出,這一步是在調用容器提供的BeanFactoryPostProcessor接口,此接口是Spring提供的擴展接口之一,我們后文再詳細討論

6. 第六步調用registerBeanPostProcessors,此方法用于注冊BeanPostProcessor,用于對bean的創(chuàng)建做攔截處理,這一步會創(chuàng)建PostProcessor的對象,BeanPostProcessor接口也是Spring提供的一個擴展接口,后面再詳細討論

7. 第七步是initMessageSource,用于初始化MesasgeSource

8. 第八步是initApplicationEventMulticaster,用于初始化事件廣播器,Spring在初始化的前后可以廣播ApplicationEvent,用戶可自定義ApplicationListener來響應這些事件

9. 第九步調用onRefresh,此方法也是用于擴展,子類中可用它實現(xiàn)其它特殊bean的裝配和定制

10. 第十步調用registerListeners,這一步是識別出Spring中配置的ApplicationListener對象

11. 第十一步調用finishBeanFactoryInitialization,創(chuàng)建所有非lazy的單例對象

12. 第十二步finishRefresh,完成初始化,此方法中會觸發(fā)Lifecycle接口,以及廣播事件,觸發(fā)ApplicationListener

Spring初始化的整個過程非常清晰,整個流程見如下refresh方法的代碼:

從整個初始化的大方法中,不難發(fā)現(xiàn),ApplicationContext提供了以下BeanFactory沒有的能力:

1. BeanFactoryPostProcessor,對BeanFactory做攔截

2. MessageSource,可用于實現(xiàn)消息的轉換,國際化等功能

3. 事件廣播機制,可對依賴注入民容器的狀態(tài)做監(jiān)聽

4. 更加可擴展,可定制化

看完了初始化的大方法后,我們再來看看obtainFreshBeanFactory方法,此方法用于獲取BeanFactory對象,其實現(xiàn)如下:

這個方法實現(xiàn)非常簡潔,其中重要的一步便是refreshBeanFactory方法的調用,此方法是抽象方法,子類可實現(xiàn)以各種不同的方式創(chuàng)建BeanFactory,比如AbstractXmlApplicationContext中通過讀取xml配置來創(chuàng)建BeanFactory,而GenericApplicationContext實現(xiàn)類中中則只是創(chuàng)建BeanFactory,沒有注冊BeanDefinition對象,需要手動編碼注冊。

????????RefreshBeanFactory方法的實現(xiàn)之一AbstractRefreshableApplicationContext中的實現(xiàn)是對xml的加載相關的特定邏輯,如下代碼:

loadBeanDefinitions方法在AbstractRefreshableApplicationContext中是一個抽象方法,用于從xml中加載Bean,AbstractRefreshableApplicationContext中不關注如何加載,其子類的實現(xiàn)類中處理如何加載bean,AbstractXmlApplicationContext中實現(xiàn)了loadBeanDefinitions方法,其實現(xiàn)中通過XmlBeanDefinitionReader加載,而ClassPathXmlApplicationContext類中則實現(xiàn)從類路徑下加載xml配置同時FileSystemXmlApplicationContext類則實現(xiàn)了從文件系統(tǒng)中的文件路徑下加載xml配置。AbstractXmlApplicationContext中的loadBeanDefinitions方法中使用了XmlBeanDefinitionReader對象,使用方式如下 :

從上圖中可以看到,XmlBeanDefinitionReader中使用到了ResourceLoader接口,此接口定義如下:

ResourceLoader接口用于加載資源,Spring的AbstractXmlApplicationContext實現(xiàn)了此接口,用于加載配置。AbstractXmlApplicationContext中的getResource方法子類也可覆蓋,比如其中一個子類FileSystemXmlApplicationContext則覆蓋了此方法,用于加載文件系統(tǒng)中的xml文件。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容