javascript
spring依赖注入_Spring源码阅读:Spring依赖注入容器
依賴注入
依賴注入是Spring框架最核心的能力,Spring框架提供的AOP,WebMVC等其它功能都是以依賴注入容器作為基礎構建的,Spring依賴注入容器類似于一個用于組裝對象的框架內核,任何應用或者基于Spring的框架都能從中受益
核心概念
理解一個項目,我習慣從它的領域模型開始,領域模型即項目中的核心概念,Spring的依賴注入容器中有哪些核心概念?
· bean是spring中的核心概念之一,是spring操作的實體,對于bean,spring中有BeanDefinition接口與之對應,BeanDefinition接口表示bean的定義,它有多個實現類,比如GenericBeanDefinition、AnnotatedGenericBeanDefinition等,每個不同的實現類針對特定的配置方式
· BeanFactory是spring的服務域,用于管理bean,BeanFactory也有多個實現類和抽象類,用于實現依賴注入容器的不同的使用方式
· ApplicationContext是用于對BeanFactory做增強,提供了BeanFactory的生命周期管理,狀態變更廣播,注解配置等功能
核心類之間的關系
在介紹Spring的框架設計原則之前,我們先通過一個類圖來看一下Spring依賴注入容器的核心類之間的關系:
這里是Spring ApplicationContext的繼承結構中的幾個有代表性的類,由于相差類較多,這里省略了繼承關系中間的抽象類,有興趣的讀者可以自己翻閱Spring的源代碼,看看其實現細節。ApplicationContext的繼承體系使用了模板方法模式,Spring中不同的抽象類為不同類型的ApplicationContext提供了基本的支持,比如AbstractXmlApplicationContext是通過xml配置使用的ApplicationContext的抽象實現。抽象類中實現了ApplicationContext應該具備的共同特性和邏輯相同的方法,具體類中再實現具體的不同點的邏輯,當然,子類中可以覆蓋那些抽象類中的方法,以便對ApplicationContext做擴展。
我們先簡單的看一下ClassPathXmlApplicationContext類,很多初學者最初就是從ClassPathXmlApplicationContext類開始接觸Spring的ApplicationContext對象的,這類是通過加載classpath下的xml配置來初始化Spring的依賴注入容器,并完成bean的裝載和依賴對象的注入過程,此類的使用步驟如下:
1. 添加Spring的配置文件,比如/META-INF/service.xml
2. 在service.xml中配置需要的bean
3. 通過service.xml創建ClassPathApplicationContext對象:
前面講述了常用的ClassPathXmlApplicationContext,現在這里再看一下一個不常用,卻更能說明ApplicationContext的結構的類GenericApplicationContext,從上面的繼承關系圖中可以看出此類實現了BeanDefinitionRegistry接口,BeanDefinitionRegistry接口有什么作用呢?我們在使用Spring的依賴注入容器前,都需要通過XML或者注解的方式向Spring中配置bean,Spring在解析xml或者注解的同時,會將每一個配置解析成一個BeanDefinition對象注冊到spring容器中,BeanDefinitionRegistry接口就是用來做bean的注冊的,可以先看一下BeanDefinitionRegistry接口的定義:
可以看到,BeanDefinitionRegistry接口提供了Bean的注冊相關的方法,可以用此接口向Spring中注冊BeanDefinition對象,移除BeanDefinition對象,獲取已注冊的BeanDefinition對象等,GenericApplicationContext類沒有配置加載功能,無法通過xml配置完成初始化,如果想要使用GenericApplicationContext類,則需要以編碼的方式向Spring中注冊Bean,例如:
我們再回到前文講到的ClassPathXmlApplicationContext,ClassPathXmlApplicationContext是GenericApplicationContext的子類,只不過ClassPathXmlApplicationContext通過解析xml的方式來完成對BeanDefinition的注冊前面講到的示例中,在xml中配的配置就類似于以繁瑣的編碼的方式使用GenericApplicationContext時通過代碼創建的BeanDefinition對象,如果我們打開ClassPathXmlApplicationContext及其父類AbstractXmlApplicationContext類,會發現它們有如下結構 (為了簡單起見,這里同樣省略了中間類,XmlBeanDefinitionReader實際上在AbstractXmlApplicationContext中):使用ClassPathXmlApplicationContext后,注冊Bean的過程由GenericApplicationContext的手動編碼變成了的通過xml配置,注冊Bean的過程由XmlBeanDefinitionReader負責,配置只是編碼方式的一種簡化,它并不是框架的核心XmlBeanDefinitionReader的執行過程后文會有詳細講解
框架的初始化
上一節講過的GenericApplicationContext的使用中,調用了一次refresh方法完成了初始化,同樣,ClassPathXmlApplicationContext也需要通過refresh方法完成初始化,只不過默認是自動調用了refresh方法,如果打開ClassPathXmlApplicationContext的源碼,我們可以看到多個構造器,其中有一個構造器可以指定是否自動刷新依賴注入容器完成依賴注入窗口的初始化,如果想要手動初始化,我們可以換使用如下方式創建ClassPathXmlApplicationContext對象:
通過IDE的幫助,我們很容易能找到refresh方法的聲明,它聲明在ApplicationContext的一個子接口ConfigurabeApplicationContext中,我們暫且先不解析此接口的作用,我們可以先看一看refresh方法的實現。
打開AbstractApplicationContext類的refresh方法,這是ApplicationContext的初始化入口,這里先介紹refresh方法的結構:
· 首先被調用的是prepareRefresh方法,此方法是AbstractApplicationContext的模板方法模式的擴展方法,子類可覆蓋此方法,用于在初始化前做前置準備
· 其次再是獲取ConfigurableListableBeanFactory對象,ApplicationContext并不是從頭開始做依賴注入容器,它是建立在BeanFactory之上,BeanFactory是Spring實現依賴注入的最核心的接口
· 第三個步驟是調用prepareBeanFactory,此方法用于向Spring中注冊默認的單例bean以及默認的BeanPostProcessor等
· 第四步是調用postProcessBeanFactory,此方法用于執行準備好了BeanFactory的環境后的后置邏輯,子類可覆蓋它實現特定邏輯
· 第五步是invokeBeanFactoryPostProcessors,從方法名上可以看出,這一步是在調用容器提供的BeanFactoryPostProcessor接口,此接口是Spring提供的擴展接口之一,我們后文再詳細討論
· 第六步調用registerBeanPostProcessors,此方法用于注冊BeanPostProcessor,用于對bean的創建做攔截處理,這一步會創建PostProcessor的對象,BeanPostProcessor接口也是Spring提供的一個擴展接口,后面再詳細討論
· 第七步是initMessageSource,用于初始化MesasgeSource
· 第八步是initApplicationEventMulticaster,用于初始化事件廣播器,Spring在初始化的前后可以廣播ApplicationEvent,用戶可自定義ApplicationListener來響應這些事件
· 第九步調用onRefresh,此方法也是用于擴展,子類中可用它實現其它特殊bean的裝配和定制
· 第十步調用registerListeners,這一步是識別出Spring中配置的ApplicationListener對象
· 第十一步調用finishBeanFactoryInitialization,創建所有非lazy的單例對象
· 第十二步finishRefresh,完成初始化,此方法中會觸發Lifecycle接口,以及廣播事件,觸發ApplicationListener
Spring初始化的整個過程非常清晰,整個流程見如下refresh方法的代碼:
從整個初始化的大方法中,不難發現,ApplicationContext提供了以下BeanFactory沒有的能力:
· BeanFactoryPostProcessor,對BeanFactory做攔截
· MessageSource,可用于實現消息的轉換,國際化等功能
· 事件廣播機制,可對依賴注入民容器的狀態做監聽
· 更加可擴展,可定制化
看完了初始化的大方法后,我們再來看看obtainFreshBeanFactory方法,此方法用于獲取BeanFactory對象,其實現如下:
這個方法實現非常簡潔,其中重要的一步便是refreshBeanFactory方法的調用,此方法是抽象方法,子類可實現以各種不同的方式創建BeanFactory,比如AbstractXmlApplicationContext中通過讀取xml配置來創建BeanFactory,而GenericApplicationContext實現類中中則只是創建BeanFactory,沒有注冊BeanDefinition對象,需要手動編碼注冊。
RefreshBeanFactory方法的實現之一AbstractRefreshableApplicationContext中的實現是對xml的加載相關的特定邏輯,如下代碼:
loadBeanDefinitions方法在AbstractRefreshableApplicationContext中是一個抽象方法,用于從xml中加載Bean,AbstractRefreshableApplicationContext中不關注如何加載,其子類的實現類中處理如何加載bean,AbstractXmlApplicationContext中實現了loadBeanDefinitions方法,其實現中通過XmlBeanDefinitionReader加載,而ClassPathXmlApplicationContext類中則實現從類路徑下加載xml配置同時FileSystemXmlApplicationContext類則實現了從文件系統中的文件路徑下加載xml配置。AbstractXmlApplicationContext中的loadBeanDefinitions方法中使用了XmlBeanDefinitionReader對象,使用方式如下 :
從上圖中可以看到,XmlBeanDefinitionReader中使用到了ResourceLoader接口,此接口定義如下:
ResourceLoader接口用于加載資源,Spring的AbstractXmlApplicationContext實現了此接口,用于加載配置。AbstractXmlApplicationContext中的getResource方法子類也可覆蓋,比如其中一個子類FileSystemXmlApplicationContext則覆蓋了此方法,用于加載文件系統中的xml文件。
Spring模板方法模式的擴展
說了這么多Spring中使用的模板方法模式,這里舉個例子,通過freemarker處理spring的xml配置,即在Spring的xml配置中可使用velocity語法,這里通過擴展Spring中的常用類ClassPathXmlApplicationContext類,并覆蓋其getResource方法,在覆蓋的方法中對Spring加載的xml配置做freemarker的處理,先看看代碼如何實現:
實現代碼后,再來看看如何使用,我們先準備好一個屬性文件,用于創建velocity的Context對象并供velocity做渲染,我這里的路徑為META-INF/config/app.properties 我這里只是一個示例,創建一個簡單的屬性文件即可:
這里只有一個key為default_username的屬性,下面再來寫一個使用了velocity語法的xml配置文件,作為示例,只寫一個簡單的xml配置文件,其中只有一個bean并且只使用velocity的if語法,其中使用了前面創建的屬性文件中的default_username屬性,判斷如果default_username的值為test,則創建bean,否則什么都不做。因為前面的屬性文件中的default_username的值確實為test,所以if判斷會滿足,我們可以從ApplicationContext中獲取到配置的bean。我這里的xml路徑為META-INF/freemarkerApp.xml.ftl,其內容如下 :
配置好了bean之后,最后再來創建ApplicationContext對象,過程如下圖所示:
整個示例到此結束
總結
以上是生活随笔為你收集整理的spring依赖注入_Spring源码阅读:Spring依赖注入容器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中国90后DNF世界冠军武神陈亮去世 疑
- 下一篇: 手机厂商开始卷内存了!这背后“微信”们得