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