前面3篇分別介紹了IoC容器與Bean的關係、Bean與Bean之間的關係以及Bean自身的控制和管理。在瞭解Spinrg核心模式時,必定要謹記他的基本工做元素就是IoC容器和Bean,全部的功能是圍繞着這2者展開的。要實現的內容無非就是經過設計模式來解決IoC與Bean的關係、Bean與Bean的關係、IoC與IoC的關係,以及對Ioc和Bean的控制。html
看完整個Spring Core的API Doc,你也不會發現任何一個名爲IoC的類或者接口。惟一一個提到IoC這個詞的是spring-beans工程下關於org.springframework.beans.factory這個包的介紹——"The core package implementing Spring's lightweight Inversion of Control (IoC) container."。實際上Spring核心框架將對IoC容器的控制都交給了BeanFactory和ApplicationContext兩個接口。java
這個2個接口有什麼關係嗎?一個叫Factory,一個叫Context,概念上徹底是兩碼事,前者是建立模式的FLAG,後者是行爲模式的FLAG。而且在工程結構上,一個屬於spring-beans,另一個屬於spring-context。不過仔細看會發現ApplicationContext繼承自BeanFactory的派生接口( ListableBeanFactory、HierarchicalBeanFactory )。要想理解他們的關係和做用,還得一個一個來講。面試
在Spring核心工程中,BeanFactory及其派生被定義爲「Ioc容器的輕量級實現」。這也是Spring最基礎的IoC容器和Bean的管理接口。factory包中主要涉及5個接口BeanFactory、ListableBeanFactory、HierarchicalBeanFactory、ConfigurableBeanFactory和ConfigurableListableBeanFactory。初來咋到看到這5個接口八成懵逼,少數牛逼的碼友估計能從名字猜想出他們的功能。其實他們有很清晰的層次結構,一層繼承一層,一層擴展一層的功能。spring
(圖片來源於他人博客,若有侵權請告知)編程
BeanFactory是IoC容器最基本的功能,他就是前面文章中一直提到的IoC設計模式的具體實現——處理IoC與Bean,Bean與Bean的關係。能夠理解BeanFactory自身就是一個IoC容器,而後提供了getter、is、contains接口來獲取和判斷Bean的狀態。對於單例或多例,BeanFactory只提供了BeanFactory::isSingleton和BeanFactory::isPrototype2個方法,這也是爲何我在設計模式與IoC一文中會說從設計模式的角度來講,Bean除了工廠方法外,只涉及singleton和prototype2個建立模式。設計模式
ListableBeanFactory繼承自BeanFactory接口。看方法會發現,BeanFactory只提供單個Bean的操做,而ListableBeanFactory都是支持列表操做,好比獲取Bean的總數、獲取Bean的name列表、經過Bean的Type獲取Bean的列表、根據註解獲取Bean的列表等。從字面上的Listable也能夠看出來其是在基本Factory的基礎上擴展了相同類型、相同名稱Bean的功能。網絡
HierarchicalBeanFactory從字面上就應該大概知道他的做用的解決層級問題,提供子父容器的管理方法。框架
再往下就是ConfigurableListableBeanFactory接口,這個接口提供的IoC控制功能,從子字面Configurable來看意思就是能夠配置的。頂層的幾個接口(BeanFactory、ListableBeanFactory)都沒提供Setter或Creater的功能,而ConfigurableListableBeanFactory集成的3個接口ListableBeanFactory、AutowireCapableBeanFactor、ConfigurableBeanFactory提供了Setter、Creater的功能。實際上ConfigurableListableBeanFactory就是提供了Beans的「增改」功能,以及一些附加的依賴控制。socket
網上關於BeanFactory及其派生結構介紹的資料不少,大部分都是從源碼的角度詳細說明他們之間的依存關係。不過從使用者的角度,實際上從整體上去了解他的組合模式思路,比你一個一個的去看源碼有意義得多,更況且就算你如今看了源碼,一年不碰你還能記得?總不能每天還複習吧?看源碼主要是要理解做者針對實現所用設計模式。固然你要是要參加什麼面試的話,還真得複習複習。記得之前我做爲面試官曾叫別人當場寫出ConfigurableListableBeanFactory的繼承關係。如今想一想當時本身有多腦殘,被面試的那些小哥估計想拍我吧?開發能力的好壞是一種思惟方式,而不是誰記得2個 new String("A")到底建立了幾個String實例,或者Integer的0到128會被預設。學習
實際上進通過多年的發展,Spring Core 的BeanFactory這塊已經發生了屢次改變。從最基礎的BeanFactory到ConfigurableListableBeanFactory層層向下推動全是接口或抽象類,每個接口都在父接口的基礎上包裝了的新的接口方法。經過多層繼承,官方的代碼中只有一個名爲DefaultListableBeanFactory的類將全部的接口功能都實現了,而後XmlFactory又繼承實現了資源讀寫的功能。XmlFactory並無多少代碼,Ioc的核心功能都在DefaultListableBeanFactory。
從設計模式上來講,很難去定義這麼多接口派生可是一個實現類提供全部功能的模式到底算什麼。我我的認爲這很像外觀模式(Facade Pattern)和裝飾模式(Decorator Pattern)結合。現實中咱們沒也不必像教科書似的模式來理解應用。下面解釋這個思路。
首先咱們來看看效果。
例如如今你用BeanFactory來「裝載」ConfigurableListableBeanFactory的實例:
BeanFactory ioc = new ConfigurableListableBeanFactory();
這個時候對你來講,這個BeanFactory就是一個裝飾器或外觀,若是BeanFactory接口不發生改變,你所能用的功能僅僅是BeanFactory提供的幾個接口方法。即便可能有人在以後的任什麼時候間修改增長了ConfigurableListableBeanFactory的方法。此時儘管ConfigurableListableBeanFactory這個實現類的本質發生了改變,可是對於BeanFactory的使用位置來講一切照舊,他經過 BeanFactory這個外觀裝飾接口看到的效果和以前如出一轍。 而擴展了接口以後的實現類,新的代碼能夠用新的接口。例如:
ConfigurableListableBeanFactory ioc = new ConfigurableListableBeanFactory();
而後咱們還能夠繼續向下擴展接口和功能。
若是你看過源碼,你會發現spring beans的BeanFactory代碼最先的編寫時間停留在2001年4月13號,距今已經很長的歷史了。相信以後確定不斷演進擴展了大量的功能。而經過接口派生的實現外觀的方式,讓古老的代碼和後續的新功能友好的共存。對於咱們本身的設計系統或實現「代碼級別的迭代」這是一個極好的例子——僅維護一個實現,經過增長外觀或裝飾器來演進功能,使用者一直都是看到的外觀。雖然這樣作彷佛會違背類的單一職責的原則。
在接下來介紹ApplicationContext以前先要說明,咱們如今建立一個Spring應該不使用任何BeanFactory相關的接口了,關於這一點官方文檔有明確的說明。除了在少數對內存大小有嚴苛要求的受限制的設備上進行嵌入式開發,其餘時候都應該使用ApplicationContext。ApplicationContext實現了BeanFactory的全部功能,並對應用開發提供了不少有用的擴展。BeanFactory如今存在的主要做用是爲一些歷史第三方庫提供支持,如今對於大多數使用Spring的用戶來講他是一個歷史性的接口。
不知作別人在學習編程開發的早年看到Context這個詞是什麼感受,反正我是蒙逼了好久。也不知道這詞最先是哪位哥翻譯的,譯成「上下文」?!英文裏con-前綴表示彙集、集合嗎,context的字面意思明明就是一堆數據的集合吧。其實碼界相似讓人翻譯的翻譯還真很多,handle=句柄(deal with,處理器)、socket=套接字(就使用原意插座還好理解)。更狠的是Robustness,真不知道在哪年是哪位大爺出於什麼緣由把他翻成「魯棒性」的。
回到正題,我真正理解Context是在開始瞭解設計模式以後。在設計模式中Context的概念出如今「策略模式」,該模式的標準解釋是執行一個方法會根據當前的狀態和對象執行不一樣的「策略」,「策略」由於實現類的性質不一樣而發生改變。實際上就是用一個Context對「策略」進行包裝,而「策略」能夠根據須要調整(細節請度娘)。我直接用Spring的ApplicationContext來講明。
(圖片來源於網絡,若有侵權請告知)
ApplictionContext的繼承思路和BeanFactory相似,就再也不介紹了。在覈心包中,Spring提供的ApplicationContext實現類目前有FileSystemXmlApplication和ClassPathXmlApplicationContext(Web包裏還有Web環境專用的ApplicationContext)。
FileSystemXmlApplication和ClassPathXmlApplicationContext分別表明了2個不一樣的「策略」,在咱們使用的時候在建立ApplicationContext時肯定,而且在運行時也能夠調整。
class App implements ApplicationContextAware{ public static void main() { // 初始化爲策略1 ApplicationContext springContext = new ClassPathXmlApplicationContext("myXml.xml"); // 使用策略1的方法,使用者不用知道實現細節 System.out.println(springContext.getApplicationName()); // 修改成策略2 springContext = new FileSystemXmlApplicationContext("/myProject/myXml.xml"); // 使用策略2的方法,使用者不用知道實現細節 System.out.println(springContext.getApplicationName()); } }
如上面的代碼,咱們能夠根據咱們須要指定不一樣的「策略」。ClassPath和FIleSystem兩個類功能都差很少,最大的區別就是加載文件路徑的差別——一個從當前工做目錄、一個從整個磁盤路徑。理論上策略模式還有一個 Strategy接口來包裝策略,Spring直接將Context設定爲一個接口,而後經過不一樣的實現類整合到了一塊兒。從實現上來看策略模式並無什麼太神奇的東西,實際上仍是一個接口多個實現類。那麼看到這裏你確定已經忍不住要吼了:這到底有什麼用?不就是建立一個實例給一個接口嗎?
實際上策略模式和Context是針對分層應用而設計的,不少設計模式的資料只會說模式是什麼,可是不會提到模式的來源和立意。我所知道在設計模式中Context的最先概念是來源是來自這篇論文——Context Object A Design Pattern for Efficient Information Sharing across Multiple System Layers(直譯爲《上下文對象——多層系統的高效信息共享的設計模式》),它大體的結論是在分層應用系統中(例如MVC——view-controller-service-dao)層之間傳遞(共享)數據時,將相同適用範圍和生命週期的全部數據組合到一個Context中去傳遞能夠大大的提高分層以後開發效率——大概就是反正我全部東西都往裏面放,你用得着就用,用不着就算,也不用來和我商量要什麼了。
因此Context實際上就是按照適用範圍(Scope)而不是應用功能(functionality)劃分的一個數據對象。 這樣在層與層之間傳遞數據的時候,不管有多少個接口都傳遞同一個的Context。
例如Spring全局應用就是ApplicatonContext,把IoC和其餘全局操做方法的丟到這個Context中。因此最後咱們看到除了IoC的Bean控制接口(BeanFactory)外,他還提供資源控制接口(ResourcePatternResolver)、國際化接口(MessageSource) 、事件發佈管理接口(ApplicationEventPublisher)。這些功能並無直接的聯繫,可是他們的適用範圍都是Applicaton級別的,因此都被整合到了ApplicatonContext中。
再例如在WebApplicationContext中,一次請求相關的全部資源以及相關的接口都會整合RequestContext中,RequestContext用於Servlet到咱們自定義的Controller層傳遞數據。
ApplicationContext繼承了BeanFactory,其核心功能仍是管理IoC以及Bean。前面也提到ApplicationContext還擴展了許多功能。下圖來自於官方,表現了2者的功能差別。
Feature | BeanFactory |
ApplicationContext |
---|---|---|
Bean 初始化與設定 |
Yes |
Yes |
BeanPostProcessor註冊 |
No |
Yes |
BeanFactoryPostProcessor註冊 |
No |
Yes |
國際化支持 |
No |
Yes |
事件發佈與註冊 |
No |
Yes |
後續的文章會繼續展開介紹這些功能以及背後設計模式的含義。