javascript
[读书笔记]Spring中的容器设计详解
【1】IOC容器與依賴(lài)反轉(zhuǎn)模式
依賴(lài)反轉(zhuǎn)是指依賴(lài)對(duì)象的獲得被反轉(zhuǎn)了。依賴(lài)控制反轉(zhuǎn)的實(shí)現(xiàn)有很多方式,Spring中IOC容器是實(shí)現(xiàn)這個(gè)模式的載體,它可以在對(duì)象生成或初始化時(shí)直接將數(shù)據(jù)注入到對(duì)象中,也可以通過(guò)將對(duì)象引用注入到對(duì)象數(shù)據(jù)域中的方式來(lái)注入對(duì)方法調(diào)用的依賴(lài)。這種依賴(lài)注入是可以遞歸的,對(duì)象被逐層注入。
通過(guò)使用IOC容器,對(duì)象依賴(lài)關(guān)系的管理被反轉(zhuǎn)了,轉(zhuǎn)到IOC容器中來(lái)了,對(duì)象之間的相互依賴(lài)關(guān)系由IOC容器進(jìn)行管理,并由IOC容器完成對(duì)象的注入。
這里的“反轉(zhuǎn)”可以認(rèn)為是“責(zé)任”的反轉(zhuǎn),把責(zé)任交給了容器。也就是說(shuō)依賴(lài)對(duì)象的獲得被反轉(zhuǎn)了,把控制權(quán)從具體業(yè)務(wù)對(duì)象手中轉(zhuǎn)交到平臺(tái)或者框架手中。
在Spring中,Spring IOC提供了一個(gè)基本的JavaBean容器,通過(guò)IOC模式管理依賴(lài)關(guān)系,并通過(guò)依賴(lài)注入和AOP切面增強(qiáng)了為JavaBean這樣的POJO對(duì)象賦予事務(wù)管理、生命周期管理等基本功能。
通過(guò)使用IOC容器,對(duì)象依賴(lài)關(guān)系的管理被反轉(zhuǎn)了,轉(zhuǎn)到IOC容器中。對(duì)象之間的相互依賴(lài)關(guān)系由IOC容器進(jìn)行管理,并由IOC容器完成對(duì)象的注入。
在Spring IOC容器的設(shè)計(jì)中,我們可以看到兩個(gè)主要的容器系列:
- 一個(gè)是實(shí)現(xiàn)BeanFactory接口的簡(jiǎn)單容器系列,這系列容器只實(shí)現(xiàn)了容器的最基本功能;
 - 另一個(gè)是ApplicationContext應(yīng)用上下文,它作為容器的高級(jí)形態(tài)而存在。應(yīng)用上下文在簡(jiǎn)單容器的基礎(chǔ)上,增加了許多面向框架的特性,同時(shí)對(duì)應(yīng)用環(huán)境作了許多適配。
 
BeanFactory體現(xiàn)了Spring為提供給用戶(hù)使用的IOC容器所設(shè)定的最基本的功能規(guī)范。在這些Spring提供的基本IOC容器的接口定義和實(shí)現(xiàn)的基礎(chǔ)上,Spring通過(guò)定義BeanDefinition來(lái)管理基于Spring的應(yīng)用中的各種對(duì)象以及它們之間的相互依賴(lài)關(guān)系。
BeanDefinition抽象了我們對(duì)Bean的定義,是讓容器起作用的主要數(shù)據(jù)類(lèi)型。我們都知道,在計(jì)算機(jī)世界里,所有的功能都是建立在通過(guò)數(shù)據(jù)對(duì)現(xiàn)實(shí)進(jìn)行抽象的基礎(chǔ)上的。IOC容器是用來(lái)管理對(duì)象依賴(lài)關(guān)系的,對(duì)IOC容器來(lái)說(shuō),BeanDefinition就是對(duì)依賴(lài)反轉(zhuǎn)模式中管理的對(duì)象依賴(lài)關(guān)系的數(shù)據(jù)抽象,也是容器實(shí)現(xiàn)依賴(lài)反轉(zhuǎn)功能的核心數(shù)據(jù)結(jié)構(gòu),依賴(lài)反轉(zhuǎn)功能都是圍繞對(duì)整個(gè)BeanDefinition的處理來(lái)完成的。這些BeanDefinition就像是容器里裝的水,有了這些基本數(shù)據(jù),容器才能夠發(fā)揮作用。
BeanFactory 一些常見(jiàn)接口釋義
① BeanDefinitionRegistry注冊(cè)表
Spring 配置文件中每一個(gè)節(jié)點(diǎn)元素在 Spring 容器里都通過(guò)一個(gè) BeanDefinition 對(duì)象表示,它描述了 Bean 的配置信息。而 BeanDefinitionRegistry 接口提供了向容器手工注冊(cè) BeanDefinition 對(duì)象的方法。
② BeanFactory 頂層接口
位于類(lèi)結(jié)構(gòu)樹(shù)的頂端 ,它最主要的方法就是 getBean(String beanName),該方法從容器中返回特定名稱(chēng)的 Bean,BeanFactory 的功能通過(guò)其他的接口得到不斷擴(kuò)展。
 
③ ListableBeanFactory
該接口定義了訪問(wèn)容器中 Bean 基本信息的若干方法,如查看 BeanDefinition的個(gè)數(shù)、獲取某一類(lèi)型 Bean的配置名、查看容器中是否包括某一 BeanDefinition等方法。
 
④ HierarchicalBeanFactory父子級(jí)聯(lián)
父子級(jí)聯(lián) IoC 容器的接口,子容器可以通過(guò)接口方法訪問(wèn)父容器。 通過(guò)HierarchicalBeanFactory 接口, Spring 的 IoC 容器可以建立父子層級(jí)關(guān)聯(lián)的容器體系,子容器可以訪問(wèn)父容器中的 Bean,但父容器不能訪問(wèn)子容器的 Bean。
public interface HierarchicalBeanFactory extends BeanFactory {@NullableBeanFactory getParentBeanFactory();boolean containsLocalBean(String name); }Spring 使用父子容器實(shí)現(xiàn)了很多功能,比如在Spring MVC 中,展現(xiàn)層 Bean 位于一個(gè)子容器中,而業(yè)務(wù)層和持久層的 Bean 位于父容器中。這樣,展現(xiàn)層 Bean 就可以引用業(yè)務(wù)層和持久層的 Bean,而業(yè)務(wù)層和持久層的 Bean 則看不到展現(xiàn)層的 Bean。
⑤ ConfigurableBeanFactory
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry是一個(gè)重要的接口,增強(qiáng)了 IoC 容器的可定制性,它定義了設(shè)置類(lèi)裝載器、屬性編輯器、容器初始化后置處理器等方法。
⑥ AutowireCapableBeanFactory 自動(dòng)裝配
定義了將容器中的 Bean 按某種規(guī)則(如按名字匹配、按類(lèi)型匹配等)進(jìn)行自動(dòng)裝配的方法。
 
⑦ SingletonBeanRegistry運(yùn)行期間注冊(cè)單例Bean
定義了允許在運(yùn)行期間向容器注冊(cè)單實(shí)例 Bean 的方法;對(duì)于單實(shí)例( singleton)的 Bean 來(lái)說(shuō),BeanFactory 會(huì)緩存 Bean 實(shí)例,所以第二次使用 getBean() 獲取 Bean 時(shí)將直接從 IoC 容器的緩存中獲取 Bean 實(shí)例。
Spring 在 DefaultSingletonBeanRegistry 類(lèi)中提供了一個(gè)用于緩存單實(shí)例 Bean 的緩存器,它是一個(gè)用 HashMap 實(shí)現(xiàn)的緩存器,單實(shí)例的 Bean 以 beanName 為鍵保存在這個(gè) HashMap 中。
public interface SingletonBeanRegistry {void registerSingleton(String beanName, Object singletonObject);@NullableObject getSingleton(String beanName);boolean containsSingleton(String beanName);String[] getSingletonNames();int getSingletonCount();Object getSingletonMutex(); }⑧ ConfigurableListableBeanFactory
除了ConfigurableBeanFactory提供的功能之外,它還提供了分析和修改bean定義以及預(yù)實(shí)例化單例的方法。這個(gè)接口只是為了允許框架內(nèi)部即插即用,即使在需要訪問(wèn)bean工廠配置方法時(shí)也是如此。
public interface ConfigurableListableBeanFactoryextends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactoryApplicationContext 一些常見(jiàn)接口釋義
① ClassPathXmlApplicationContext
默認(rèn)從類(lèi)路徑加載配置文件
② FileSystemXmlApplicationContext
默認(rèn)從文件系統(tǒng)中裝載配置文件
③ ApplicationEventPublisher
讓容器擁有發(fā)布應(yīng)用上下文事件的功能,包括容器啟動(dòng)事件、關(guān)閉事件等。
④ MessageSource
為應(yīng)用提供 i18n 國(guó)際化消息訪問(wèn)的功能;
⑤ ResourcePatternResolver
所 有 ApplicationContext 實(shí)現(xiàn)類(lèi)都實(shí)現(xiàn)了類(lèi)似于PathMatchingResourcePatternResolver 的功能,可以通過(guò)帶前綴的 Ant 風(fēng)格的資源文件路徑裝載 Spring 的配置文件。
⑥ LifeCycle
該接口是 Spring 2.0 加入的,該接口提供了 start()和 stop()兩個(gè)方法,主要用于控制異步處理過(guò)程。在具體使用時(shí),該接口同時(shí)被 ApplicationContext 實(shí)現(xiàn)及具體 Bean 實(shí)現(xiàn),ApplicationContext 會(huì)將 start/stop 的信息傳遞給容器中所有實(shí)現(xiàn)了該接口的 Bean,以達(dá)到管理和控制 JMX、任務(wù)調(diào)度等目的。
⑦ ConfigurableApplicationContext
擴(kuò)展于 ApplicationContext,它新增加了兩個(gè)主要的方法:refresh()和 close(),讓 ApplicationContext 具有啟動(dòng)、刷新和關(guān)閉應(yīng)用上下文的能力。在應(yīng)用上下文關(guān)閉的情況下調(diào)用 refresh()即可啟動(dòng)應(yīng)用上下文,在已經(jīng)啟動(dòng)的狀態(tài)下,調(diào)用 refresh()則清除緩存并重新裝載配置信息,而調(diào)用 close()則可關(guān)閉應(yīng)用上下文。
⑧ WebApplicationContext
是專(zhuān)門(mén)為 Web 應(yīng)用準(zhǔn)備的,它允許從相對(duì)于 Web 根目錄的路徑中裝載配置文件完成初始化工作。從 WebApplicationContext 中可以獲得 ServletContext 的引用,整個(gè) Web 應(yīng)用上下文對(duì)象將作為屬性放置到 ServletContext 中,以便 Web 應(yīng)用環(huán)境可以訪問(wèn) Spring 應(yīng)用上下文。
【2】Spring IOC容器的設(shè)計(jì)
下圖描述了Spring IOC容器中的主要接口設(shè)計(jì)。
 
① 從接口BeanFactory到HierarchicalBeanFactory,再到ConfigurableBeanFactory,是一條主要的BeanFactory設(shè)計(jì)路徑。
在這條接口設(shè)計(jì)路徑中,BeanFactory接口定義了基本的IOC容器的規(guī)范。在這個(gè)接口定義中,包括了getBean()這樣的IOC容器的基本方法(通過(guò)這個(gè)方法可以從容器中取得Bean)。
而HierarchicalBeanFactory接口在繼承了BeanFactory的基本接口之后,增加了getParentBeanFactory()的接口功能,使BeanFactory具備了雙親IOC容器的管理功能。
在接下里的ConfigurableBeanFactory接口中,主要定義了一些對(duì)BeanFactory的配置功能,比如通過(guò)setParentBeanFactory設(shè)置雙親IOC容器,通過(guò)addBeanPostProcessor配置Bean后置處理器等等。通過(guò)這些接口設(shè)計(jì)的疊加,定義了BeanFactory就是簡(jiǎn)單IOC容器的基本功能。
BeanFactory有三個(gè)二級(jí)子接口:HierarchicalBeanFactory、AutowireCapableBeanFactory和ListableBeanFactory。
ConfigurableBeanFactory可以被稱(chēng)為三級(jí)接口,對(duì)二級(jí)接口HierarchicalBeanFactory進(jìn)行了再次增強(qiáng),它還繼承了另一個(gè)外來(lái)的接口SingletonBeanRegistry。
ConfigurableListableBeanFactory可以被稱(chēng)為四級(jí)接口/三級(jí)接口,其繼承了ListableBeanFactory、AutowireCapableBeanFactory、ConfigurableBeanFactory。這4級(jí)接口是BeanFactory的基本接口體系。
ListableBeanFactory 接口表示這些 Bean 是可列表的,而 HierarchicalBeanFactory 表示的是這些Bean 是有繼承關(guān)系的,也就是每個(gè)Bean 有可能有父 Bean。BeanAutowireCapableBeanFactory 接口定義Bean 的自動(dòng)裝配規(guī)則。
② 第二條設(shè)計(jì)主線是以ApplicationContext應(yīng)用上下文接口為核心的接口設(shè)計(jì)。
這里涉及的主要設(shè)計(jì)接口有,從BeanFactory到ListableBeanFactory,再到ApplicationContext,再到常用的WebApplicationContext或者ConfigurableApplicationContext接口。我們常用的應(yīng)用上下文基本都是ConfigurableApplicationContext或者WebApplicationContext的實(shí)現(xiàn)。在這個(gè)接口體系中,ListableBeanFactory和HiearerchialBeanFactory兩個(gè)接口,連接BeanFactory接口定義和ApplicationContext應(yīng)用上下文接口定義`。
- 在ListableBeanFactory接口中,細(xì)化了許多BeanFactory的接口功能,比如定義了getBeanDefinitionNames接口方法。
 - 對(duì)于HierarchicalBeanFactory接口,我們?cè)谇懊嬉呀?jīng)提到過(guò)其在繼承了BeanFactory的基本接口之后,增加了getParentBeanFactory()的接口功能,使BeanFactory具備了雙親IOC容器的管理功能。
 - 對(duì)于ApplicationContext接口,它通過(guò)繼承了MessageSource、ResourceLoader、ApplicationEventPublisher接口,在BeanFactory簡(jiǎn)單Ioc容器的基礎(chǔ)上添加了許多對(duì)高級(jí)容器的特性的支持。
 
圖中涉及的是主要的接口關(guān)系,而具體的Ioc容器都是在這個(gè)接口體系下實(shí)現(xiàn)的,比如DefaultListableBeanFactory,這個(gè)基本Ioc容器的實(shí)現(xiàn)就是實(shí)現(xiàn)了ConfigurableBeanFactory,從而成為了一個(gè)簡(jiǎn)單Ioc容器的實(shí)現(xiàn)。像其他的Ioc容器,比如XMLBeanFactory,都是在DefaultListableBeanFactory的基礎(chǔ)上做擴(kuò)展。同樣,ApplicationContext的實(shí)現(xiàn)也是如此。
這個(gè)接口系統(tǒng)是以BeanFactory和ApplicationContext為核心的,而B(niǎo)eanFactory又是Ioc容器的最基本的接口。在ApplicationContext的設(shè)計(jì)中,一方面,可以看到它繼承了BeanFactory接口體系中的ListableBeanFactory、AutowireCapableBeanFactory、HiearerchialBeanFactory等BeanFactory的接口,具備了BeanFactory Ioc容器的基本功能。
另外一方面,通過(guò)繼承MessageSource、ResourceLoader、ApplicationEventPublisher這些接口,BeanFactory為ApplicationContext賦予了更高級(jí)的Ioc容器特性。對(duì)于ApplicationContext而言,為了在Web環(huán)境中使用它,還設(shè)計(jì)了WebApplicationContext接口,而這個(gè)接口通過(guò)繼承ThemeSource接口來(lái)擴(kuò)充功能。
【3】BeanFactory
BeanFactory接口定義了IOC容器最基本的形式,并且提供了IOC容器所應(yīng)該遵守的最基本的服務(wù)契約。BeanFactory接口設(shè)計(jì)了getBean方法,這個(gè)方法是使用IOC容器API的主要方法,通過(guò)這個(gè)方法,可以取得IOC容器中管理的Bean,Bean的取得是通過(guò)指定名字來(lái)索引的。如果需要在獲取Bean時(shí)對(duì)Bean的類(lèi)型進(jìn)行檢查,BeanFactory接口定義了帶有參數(shù)的getBean方法,這個(gè)方法的使用與不帶參數(shù)的getBean方法類(lèi)似,不同的是增加了對(duì)Bean檢索的類(lèi)型的要求。
有了BeanFactory的定義,用戶(hù)可以執(zhí)行以下操作:
- 通過(guò)接口方法containsBean讓用戶(hù)能夠判斷容器是否含有指定名字的Bean;
 - 通過(guò)接口方法isSingleton來(lái)查詢(xún)指定名字的Bean是否是Singleton類(lèi)型的Bean。對(duì)于Singleton屬性,用戶(hù)可以在BeanDefinition中指定;
 - 通過(guò)接口方法isPrototype來(lái)查詢(xún)指定名字的Bean是否是prototype類(lèi)型的。與Singleton屬性一樣,這個(gè)屬性也可以由用戶(hù)在BeanDefinition中指定;
 - 通過(guò)接口方法isTypeMatch來(lái)查詢(xún)指定了名字的Bean的Class類(lèi)型是否是特定的Class類(lèi)型。這個(gè)Class類(lèi)型可以由用戶(hù)來(lái)指定。
 - 通過(guò)接口方法getType來(lái)查詢(xún)指定名字的Bean的Class類(lèi)型;
 - 通過(guò)接口方法getAliases來(lái)查詢(xún)指定了名字的Bean的所有別名,這些別名都是用戶(hù)在BeanDefinition中定義的。
 
BeanFactory接口源碼
public interface BeanFactory { // 用來(lái)引用一個(gè)實(shí)例,或把它和工廠產(chǎn)生的Bean區(qū)分開(kāi),就是說(shuō),如果一個(gè)FactoryBean的名字為 //a,那么,&a會(huì)得到那個(gè)FactoryString FACTORY_BEAN_PREFIX = "&";//五個(gè)不同形式的getBean方法,獲取實(shí)例Object getBean(String name) throws BeansException;//根據(jù)bean的名字和Class類(lèi)型來(lái)得到bean實(shí)例,增加了類(lèi)型安全驗(yàn)證機(jī)制。<T> T getBean(String name, Class<T> requiredType) throws BeansException;Object getBean(String name, Object... args) throws BeansException;<T> T getBean(Class<T> requiredType) throws BeansException;<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);// 判斷bean是否存在boolean containsBean(String name);// bean是否單例boolean isSingleton(String name) throws NoSuchBeanDefinitionException;// Bean是否為原型(多實(shí)例)boolean isPrototype(String name) throws NoSuchBeanDefinitionException;// 名稱(chēng)、類(lèi)型是否匹配boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;// 名稱(chēng)、類(lèi)型是否匹配boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;// 獲取類(lèi)型@NullableClass<?> getType(String name) throws NoSuchBeanDefinitionException;@NullableClass<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;// 根據(jù)實(shí)例名字獲取別名String[] getAliases(String name); }可以看到,這里定義的只是一系列的接口方法,通過(guò)這一系列的BeanFactory接口,可以使用不同的Bean的檢索方法,很方便地從IOC容器中得到需要的Bean,從而忽略具體的IOC容器的實(shí)現(xiàn)。
也就是說(shuō)在BeanFactory里只對(duì)IOC容器的基本行為作了定義,根本不關(guān)心你的bean是如何定義怎樣加載的。正如我們只關(guān)心工廠里得到什么的產(chǎn)品對(duì)象,至于工廠是怎么生產(chǎn)這些對(duì)象的,這個(gè)基本的接口不關(guān)心。
【4】BeanFactory容器的設(shè)計(jì)原理
BeanFactory接口提供了使用IOC容器的規(guī)范,在這個(gè)基礎(chǔ)上,Spring還提供了符合這個(gè)IOC容器接口的一系列容器的實(shí)現(xiàn)供開(kāi)發(fā)人員使用,這里以XmlBeanFactory的實(shí)現(xiàn)為例來(lái)說(shuō)明簡(jiǎn)單IOC容器的設(shè)計(jì)原理。
① XmlBeanFactory類(lèi)繼承關(guān)系
可以看到,作為一個(gè)簡(jiǎn)單IOC容器系列最底層實(shí)現(xiàn)的XmlBeanFactory,與我們?cè)赟pring應(yīng)用中用到的那些上下文相比,有一個(gè)非常明顯的特點(diǎn):它只提供最基本的IOC容器的功能。理解這一點(diǎn)有助于我們理解ApplicationContext與基本的BeanFactory之間的區(qū)別和聯(lián)系。
我們可以認(rèn)為之間的BeanFactory實(shí)現(xiàn)是IOC容器的基本形式,而各種ApplicationContext的實(shí)現(xiàn)是IOC容器的高級(jí)表現(xiàn)形式。
在Spring中,實(shí)際上是把DefaultListableBeanFactory作為一個(gè)默認(rèn)的功能完整的IOC容器來(lái)使用的。XmlBeanFactory在繼承了DefaultListableBeanFactory容器的功能的同時(shí),增加了新的功能–可以讀取以XML文件方式定義的BeanDefinition。
在XmlBeanFactory中,初始化了一個(gè)XMLBeanDefinitionReader對(duì)象,有了這個(gè)Reader對(duì)象,那些以XML方式定義的BeanDefinition就有了處理的地方。構(gòu)造XmlBeanFactory這個(gè)IOC容器時(shí),需要指定BeanDefinition的信息來(lái)源,這個(gè)信息來(lái)源需要封裝成Spring中的Resource類(lèi)來(lái)給出。
Resource是Spring用來(lái)封裝IO操作的類(lèi)。比如我們的BeanDefinition信息是以XML文件形式存在的,那么可以使用像ClassPathResource res=new ClassPathResource("beans.xml");這樣具體的ClassPathResource來(lái)構(gòu)造需要的Resource,然后將Resource作為構(gòu)造參數(shù)傳遞給XmlBeanFactory構(gòu)造函數(shù)。這樣IOC容器就可以方便地定位到需要的BeanDefinition信息來(lái)對(duì)Bean完成容器的初始化和依賴(lài)注入過(guò)程。
XmlBeanFactory源碼如下:
public class XmlBeanFactory extends DefaultListableBeanFactory {//這里初始化了一個(gè)XmlBeanDefinitionReader對(duì)XML形式的信息進(jìn)行處理private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);// 使用給定的resource實(shí)例化XmlBeanFactory public XmlBeanFactory(Resource resource) throws BeansException {this(resource, null);}public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {super(parentBeanFactory);this.reader.loadBeanDefinitions(resource);} }綜合來(lái)看,XmlBeanFactory的功能是建立在DefaultListableBeanFactory這個(gè)基本容器的基礎(chǔ)上的,并在這個(gè)基本容器的基礎(chǔ)上實(shí)現(xiàn)了其他諸如XML讀取的附加功能。
如上源碼所示,在XmlBeanFactory構(gòu)造方法中需要得到Resource對(duì)象。對(duì)XmlBeanDefinitionReader對(duì)象的初始化以及使用這個(gè)對(duì)象來(lái)完成對(duì)loadBeanDefinitions的調(diào)用,就是這個(gè)調(diào)用啟動(dòng)從Resource中載入BeanDefinitions的過(guò)程,loadBeanDefinitions同時(shí)也是IOC容器初始化的重要組成部分。
我們看到XMLBeanFactory使用了DefaultListableBeanFactory作為基類(lèi),DefaultListableBeanFactory是很重要的一個(gè)IOC實(shí)現(xiàn),在其他IOC容器中,比如ApplicationContext,其實(shí)現(xiàn)的基本原理和XMLBeanFactory一樣,也是通過(guò)持有或者擴(kuò)展DefaultListableBeanFactory來(lái)獲得基本的IOC容器的功能的。
② 編程式使用IOC容器
//根據(jù)Xml配置文件創(chuàng)建Resource資源對(duì)象,該對(duì)象中包含了BeanDefinition的信息 ClassPathResource resource =new ClassPathResource("application-context.xml");//創(chuàng)建DefaultListableBeanFactory DefaultListableBeanFactory factory =new DefaultListableBeanFactory();//創(chuàng)建XmlBeanDefinitionReader讀取器,用于載入BeanDefinition。之所以需要BeanFactory作為 //參數(shù),是因?yàn)闀?huì)將讀取的信息回調(diào)配置給factory XmlBeanDefinitionReader reader =new XmlBeanDefinitionReader(factory);//XmlBeanDefinitionReader執(zhí)行載入BeanDefinition的方法,最后會(huì)完成Bean的載入和注冊(cè)。完成 //后Bean就成功的放置到IOC容器當(dāng)中,以后我們就可以從中取得Bean來(lái)使用 reader.loadBeanDefinitions(resource);這樣,我們就可以通過(guò)factory對(duì)象來(lái)使用DefaultListableBeanFactory這個(gè)IOC容器了。在使用IOC 容器時(shí),需要如下幾個(gè)步驟:
- 創(chuàng)建IOC配置文件的抽象資源,這個(gè)抽象資源包含了BeanDefinition的定義信息。
 - 創(chuàng)建一個(gè)BeanFactory,這里使用DefaultListableBeanFactory。
 - 創(chuàng)建一個(gè)載入BeanDefinition的讀取器,這里使用XmlBeanDefinitionReader來(lái)載入xml文件形式的BeanDefinition,通過(guò)一個(gè)回調(diào)配置給BeanFactory。
 - 從定義好的資源位置讀入配置信息,具體的解析過(guò)程由XmlBeanDefinitionReader來(lái)完成。完成整個(gè)載入和注冊(cè)Bean定義之后,需要的IOC容器就建立起來(lái)了。這個(gè)時(shí)候就可以直接使用IOC容器了。
 
以編程 的方式使用DefaultListableBeanFactory時(shí),首先定義一個(gè)Resource來(lái)定位容器使用的BeanDefinition。這時(shí)使用的是ClassPathResource,這意味著Spring會(huì)在類(lèi)路徑中去尋找以文件形式存在的BeanDefinition信息。
ClassPathResource resource =new ClassPathResource("application-context.xml");這里定義的Resource并不能由DefaultListableBeanFactory直接使用,Spring通過(guò)使用BeanDefinitionReader來(lái)對(duì)這些信息進(jìn)行處理。在這里,我們也可以看到使用ApplicationContext相對(duì)于直接使用DefaultListableBeanFactory的好處。因?yàn)樵贏pplicationContext中,Spring已經(jīng)為我們提供了一系列加載不同Resource的讀取器的實(shí)現(xiàn),而DefaultListableBeanFactory只是一個(gè)純粹的IOC容器,需要為它配置特定的讀取器才能完成這些功能。
【5】ApplicationContext
① 概述
在Spring中,系統(tǒng)已經(jīng)為用戶(hù)提供了許多已經(jīng)定義好的容器實(shí)現(xiàn),而不需要開(kāi)發(fā)人員事必躬親。相比那些簡(jiǎn)單拓展BeanFactory的基本IOC容器,開(kāi)發(fā)人員常用的ApplicationContext除了能夠提供前面介紹的容器的基本功能外,還為用戶(hù)提供了一些附加服務(wù),所以說(shuō)ApplicationContext是一個(gè)高級(jí)形態(tài)意義的IOC容器。
ApplicationContext接口繼承圖
 
 附加功能主要如下:
- 支持不同的信息源。我們看到ApplicationContext擴(kuò)展了MessageSource接口,這些信息源的擴(kuò)展功能可以支持國(guó)際化的實(shí)現(xiàn),為開(kāi)發(fā)多語(yǔ)言版本的應(yīng)用提供服務(wù)。
 - 訪問(wèn)資源。這一特性體現(xiàn)在對(duì)ResourceLoader和Resource的支持上,這樣我們可以從不同地方得到Bean定義資源。這種抽象使用戶(hù)程序可以靈活地定義Beean定義信息,尤其是從不同的IO途徑得到Bean定義信息。一般來(lái)說(shuō),具體ApplicationContext都是繼承了DefaultResourceLoader的子類(lèi)。因?yàn)镈efaultResourceLoader是AbstractApplicationContext的基類(lèi)。
 - 支持應(yīng)用事件。繼承了接口ApplicationEventPublisher,從而在上下文中引入了事件機(jī)制。這些事件和Bean的生命周期的結(jié)合為Bean的管理提供了便利。
 - 在ApplicationContext中提供的附加服務(wù)。這些服務(wù)使得基本IOC容器的功能更豐富。因?yàn)榫邆淞诉@些豐富的附加功能,使得ApplicationContext與簡(jiǎn)單的BeanFactory相比,對(duì)它的使用是一種面向框架的使用風(fēng)格。
 
在ApplicationContext中Spring已經(jīng)為我們提供了一系列加載不同Resource的讀取器的實(shí)現(xiàn),而類(lèi)似DefaultListableBeanFactory只是一個(gè)純粹的IOC容器,需要為它配置特定的讀取器才能完成這些功能。當(dāng)然是用DefaultListableBeanFactory這種更底層的容器,能提高定制IOC容器的靈活性。
② ApplicationContext容器的設(shè)計(jì)原理
這里以FileSystemXmlApplicationContext的實(shí)現(xiàn)為例來(lái)說(shuō)明ApplicationContext容器的設(shè)計(jì)原理。
 在FileSystemlXmlApplicationContext的設(shè)計(jì)中,我們看到ApplicationContext應(yīng)用上下文的主要功能已經(jīng)在FileSystemXmlApplicaitonContext的基類(lèi)AbstractXmlApplicationContext中實(shí)現(xiàn)了,在FileSystemXmlApplicationContext中,作為一個(gè)具體的應(yīng)用上下文,只需要實(shí)現(xiàn)和它自身設(shè)計(jì)相關(guān)的兩個(gè)功能。
一個(gè)功能是,如果應(yīng)用直接使用FileSystemlXmlApplicationContext,對(duì)于實(shí)例化這個(gè)應(yīng)用上下文的支持,同時(shí)啟動(dòng)IOC容器的refresh()過(guò)程。代碼如下所示:
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)throws BeansException {super(parent);setConfigLocations(configLocations);if (refresh) {refresh();} }這個(gè)refresh()過(guò)程會(huì)牽涉IOC容器啟動(dòng)的一系列復(fù)雜操作,同時(shí)對(duì)于不同的容器實(shí)現(xiàn),這些操作都是類(lèi)似的,因此在基類(lèi)中將它們封裝好。所以我們?cè)贔ileSystemlXmlApplicationContext的設(shè)計(jì)中看到的只是一個(gè)簡(jiǎn)單的調(diào)用。
另一個(gè)功能是與FileSystemlXmlApplicationContext設(shè)計(jì)具體相關(guān)的功能,這部分與怎樣從文件系統(tǒng)中加載XML的Bean定義資源有關(guān)。
通過(guò)這個(gè)過(guò)程,可以為在文件系統(tǒng)中讀取以XML形式存在的BeanDefinition做準(zhǔn)備,因?yàn)椴煌膽?yīng)用上下文實(shí)現(xiàn)對(duì)應(yīng)著不同的讀取BeanDefinition的方式,在FileSystemlXmlApplicationContext中的實(shí)現(xiàn)代碼如下:
protected Resource getResourceByPath(String path) {if (path.startsWith("/")) {path = path.substring(1);}return new FileSystemResource(path);}可以看到,調(diào)用這個(gè)方法,可以得到FileSystemResource的資源定位。
【6】BeanDefinition
在這些spring提供的基本IOC容器的接口定義和實(shí)現(xiàn)的基礎(chǔ)上,spring通過(guò)定義BeanDefinition來(lái)管理基于Spring的應(yīng)用中的各種對(duì)象以及他們之間的相互依賴(lài)關(guān)系。BeanDefinition抽象了我們對(duì)Bean的定義,是讓容易起作用的主要數(shù)據(jù)類(lèi)型。
我們都知道,在計(jì)算機(jī)世界里,所有的功能都是建立在通過(guò)數(shù)據(jù)對(duì)現(xiàn)實(shí)進(jìn)行抽象的基礎(chǔ)上的。IOC容器是用來(lái)管理對(duì)象依賴(lài)關(guān)系的,對(duì)IOC容器來(lái)說(shuō),BeanDefinition就是對(duì)依賴(lài)反轉(zhuǎn)模式中管理的對(duì)象依賴(lài)關(guān)系的數(shù)據(jù)抽象,也是容器實(shí)現(xiàn)依賴(lài)反轉(zhuǎn)功能的核心數(shù)據(jù)結(jié)構(gòu),依賴(lài)反轉(zhuǎn)功能都是圍繞著這個(gè)BeanDefinition的處理來(lái)完成的。
SpringIOC容器管理了我們定義的各種Bean對(duì)象及其相互的關(guān)系,Bean對(duì)象在Spring實(shí)現(xiàn)中是以BeanDefinition來(lái)描述的,其繼承體系如下:
 
 Bean 的解析過(guò)程非常復(fù)雜,功能被分的很細(xì),因?yàn)檫@里需要被擴(kuò)展的地方很多,必須保證有足夠的靈活性,以應(yīng)對(duì)可能的變化。Bean 的解析主要就是對(duì) Spring 配置文件的解析,這個(gè)解析過(guò)程主要由如下類(lèi)完成:
 
 
BeanDefinitionReader是簡(jiǎn)單的bean定義讀取器,我們常用的默認(rèn)實(shí)現(xiàn)是XmlBeanDefinitionReader,格式化讀取xml bean定義。其通常將實(shí)際讀取動(dòng)作委派給BeanDefinitionDocumentReader接口的實(shí)現(xiàn),如DefaultBeanDefinitionDocumentReader。
BeanDefinitionDocumentReader 接口源碼如下所示,其只有一個(gè)方法registerBeanDefinitions用來(lái)從給定的DOM 文檔中讀取bean定義并注冊(cè)到給定的上下文中。
public interface BeanDefinitionDocumentReader {void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)throws BeanDefinitionStoreException; }【7】IOC容器的初始化過(guò)程-引言
簡(jiǎn)單來(lái)說(shuō),IOC容器的初始化是由前面介紹的refresh()方法來(lái)啟動(dòng)的,這個(gè)方法標(biāo)志著IOC容器的正式啟動(dòng)。具體來(lái)說(shuō),這個(gè)啟動(dòng)包括BeanDefinition的Resource定位、載入和注冊(cè)三個(gè)基本過(guò)程。Spring把這三個(gè)過(guò)程分開(kāi),并使用不同的模塊來(lái)完成,如使用相應(yīng)的 ResourceLoader、BeanDefinitionReader等模塊。通過(guò)這樣的設(shè)計(jì)方式,可以讓用戶(hù)更加靈活地對(duì)這三個(gè)過(guò)程進(jìn)行剪裁或擴(kuò)展,定義出最適合自己的IOC容器的初始化過(guò)程。
① 第一個(gè)過(guò)程是Resource定位過(guò)程
這個(gè)Resource定位指的是BeanDefinition的資源定位。它由ResourceLoader通過(guò)統(tǒng)一的Resource接口來(lái)完成,這個(gè)Resource對(duì)各種形式的BeanDefinition的使用都提供了統(tǒng)一接口。
對(duì)于這些BeanDefinition的存在形式,相信大家都不會(huì)感到陌生。比如在文件系統(tǒng)中的Bean定義信息可以使用FileSystemResource來(lái)進(jìn)行抽象;在類(lèi)路徑的Bean定義信息可以使用前面提到的ClassPathResource來(lái)使用等等。這個(gè)定位過(guò)程類(lèi)似于容器尋找數(shù)據(jù)的過(guò)程。
② 第二個(gè)過(guò)程是BeanDefinition的載入
這個(gè)載入過(guò)程就是把用戶(hù)定義好得多Bean表示成IOC容器內(nèi)部的數(shù)據(jù)結(jié)構(gòu),而這個(gè)容器內(nèi)部的數(shù)據(jù)結(jié)構(gòu)就是BeanDefinition。
具體來(lái)說(shuō),這個(gè)BeanDefinition實(shí)際上就是pojo對(duì)象在IOC容器中的抽象,通過(guò)這個(gè)BeanDefinition定義的數(shù)據(jù)結(jié)構(gòu),使IOC容器能夠方便地對(duì)pojo對(duì)象也就是bean進(jìn)行管理。
③ 第三個(gè)過(guò)程是向IOC容器注冊(cè)這些BeanDefinition的過(guò)程
這個(gè)過(guò)程是通過(guò)調(diào)用BeanDefinitionRegistry接口的實(shí)現(xiàn)來(lái)完成的。這個(gè)注冊(cè)過(guò)程把載入過(guò)程中解析得到的BeanDefinition向IOC容器進(jìn)行注冊(cè)。通過(guò)分析,我們可以看到,在IOC容器內(nèi)部將BeanDefinition注入到一個(gè)HashMap中去,IOC容器就是通過(guò)這個(gè)HashM來(lái)持有這些BeanDefinition數(shù)據(jù)的。
值得注意的是,這里談的是IOC容器的初始化過(guò)程,在這個(gè)過(guò)程中,一般不包含Bean依賴(lài)注入的實(shí)現(xiàn)。在Spring IOC的設(shè)計(jì)中,Bean定義的載入和依賴(lài)注入是兩個(gè)獨(dú)立的過(guò)程。依賴(lài)注入一般發(fā)生在應(yīng)用第一次通過(guò)getBean向容器索取Bean的時(shí)候。但有一個(gè)例外值得注意,在使用IOC容器時(shí)有一個(gè)預(yù)實(shí)例化的配置,通過(guò)這個(gè)預(yù)實(shí)例化的配置(具體來(lái)說(shuō)就是Bean定義信息中的lazyinit屬性),用戶(hù)可以對(duì)容器初始化過(guò)程作一個(gè)微小的控制,從而改變這個(gè)被設(shè)置了lazyinit屬性的Bean的依賴(lài)注入過(guò)程。
舉例來(lái)說(shuō),如果我們對(duì)某個(gè)Bean設(shè)置了lazyinit屬性,那么這個(gè)Bean的依賴(lài)注入在IOC容器初始化時(shí)就預(yù)先完成了,而不需要等到整個(gè)初始化完成以后,第一次使用getBean時(shí)才會(huì)觸發(fā)。
參考博文:
 [讀書(shū)筆記](méi)Spring中IOC容器中XmlBeanFactory的初始化詳解
 [讀書(shū)筆記](méi)Spring中IOC容器中FileSystemXmlApplicationContext的初始化詳解
總結(jié)
以上是生活随笔為你收集整理的[读书笔记]Spring中的容器设计详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
                            
                        - 上一篇: Office excel2010如何用两
 - 下一篇: firefox图片不存在就显示小红叉的方