在對IoC有了初步的認知後,我們開始對IOC的實現原理進行深入理解。本文將幫助你站在設計者的角度去看IOC最頂層的結構設計。@pdai
如果讓你來設計一個IoC容器,你會怎麼設計?我們初步的通過這個問題,來幫助我們更好的理解IOC的設計。
在設計時,首先需要考慮的是IOC容器的功能(輸入和輸出), 承接前面的文章,我們初步的畫出IOC容器的整體功能。
在此基礎上,我們初步的去思考,如果作為一個IOC容器的設計者,主體上應該包含哪幾個部分:
(pdai:這種思考的過程才是建設性的,知識體系的構建才是高效的)
那麼我們來看下Spring設計者是如何設計IoC並解決這些問題的。
Spring Bean的建立是典型的工廠模式,這一系列的Bean工廠,也即IOC容器為開發者管理物件間的依賴關係提供了很多便利和基礎服務,在Spring中有許多的IOC容器的實現供使用者選擇和使用,這是IOC容器的基礎;在頂層的結構設計主要圍繞著BeanFactory和xxxRegistry進行:
- BeanFactory: 工廠模式定義了IOC容器的基本功能規範
- BeanRegistry: 向IOC容器手工註冊 BeanDefinition 物件的方法
其相互關係如下:
我們再通過幾個問題來輔助理解。
BeanFactory作為最頂層的一個介面類,它定義了IOC容器的基本功能規範,BeanFactory 有三個子類:ListableBeanFactory、HierarchicalBeanFactory 和AutowireCapableBeanFactory。我們看下BeanFactory介面:
public interface BeanFactory {
//用於取消參照範例並將其與FactoryBean建立的bean區分開來。例如,如果命名的bean是FactoryBean,則獲取將返回Factory,而不是Factory返回的範例。
String FACTORY_BEAN_PREFIX = "&";
//根據bean的名字和Class型別等來得到bean範例
Object getBean(String name) throws BeansException;
Object getBean(String name, Class 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;
//返回指定bean的Provider
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
//檢查工廠中是否包含給定name的bean,或者外部註冊的bean
boolean containsBean(String name);
//檢查所給定name的bean是否為單例/原型
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
//判斷所給name的型別與type是否匹配
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
//獲取給定name的bean的型別
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
//返回給定name的bean的別名
String[] getAliases(String name);
}
主要是為了區分在 Spring 內部在操作過程中物件的傳遞和轉化過程中,對物件的資料存取所做的限制。
有哪些介面呢?
Spring 組態檔中每一個<bean>
節點元素在 Spring 容器裡都通過一個 BeanDefinition 物件表示,它描述了 Bean 的設定資訊。而 BeanDefinitionRegistry 介面提供了向容器手工註冊 BeanDefinition 物件的方法。
Bean物件存在依賴巢狀等關係,所以設計者設計了BeanDefinition,它用來對Bean物件及關係定義;我們在理解時只需要抓住如下三個要點:
- BeanDefinition 定義了各種Bean物件及其相互的關係
- BeanDefinitionReader 這是BeanDefinition的解析器
- BeanDefinitionHolder 這是BeanDefination的包裝類,用來儲存BeanDefinition,name以及aliases等。
SpringIOC容器管理了我們定義的各種Bean物件及其相互的關係,Bean物件在Spring實現中是以BeanDefinition來描述的,其繼承體系如下
Bean 的解析過程非常複雜,功能被分的很細,因為這裡需要被擴充套件的地方很多,必須保證有足夠的靈活性,以應對可能的變化。Bean 的解析主要就是對 Spring 組態檔的解析。這個解析過程主要通過下圖中的類完成:
BeanDefinitionHolder 這是BeanDefination的包裝類,用來儲存BeanDefinition,name以及aliases等
IoC容器的介面類是ApplicationContext,很顯然它必然繼承BeanFactory對Bean規範(最基本的ioc容器的實現)進行定義。而ApplicationContext表示的是應用的上下文,除了對Bean的管理外,還至少應該包含了
- 存取資源: 對不同方式的Bean設定(即資源)進行載入。(實現ResourcePatternResolver介面)
- 國際化: 支援資訊源,可以實現國際化。(實現MessageSource介面)
- 應用事件: 支援應用事件。(實現ApplicationEventPublisher介面)
我們來看下ApplicationContext整體結構
在考慮ApplicationContext介面的實現時,關鍵的點在於,不同Bean的設定方式(比如xml,groovy,annotation等)有著不同的資源載入方式,這便衍生除了眾多ApplicationContext的實現類。
第一,從類結構設計上看, 圍繞著是否需要Refresh容器衍生出兩個抽象類:
GenericApplicationContext: 是初始化的時候就建立容器,往後的每次refresh都不會更改
AbstractRefreshableApplicationContext: AbstractRefreshableApplicationContext及子類的每次refresh都是先清除已有(如果不存在就建立)的容器,然後再重新建立;AbstractRefreshableApplicationContext及子類無法做到GenericApplicationContext混合搭配從不同源頭獲取bean的定義資訊
第二, 從載入的源來看(比如xml,groovy,annotation等), 衍生出眾多型別的ApplicationContext, 典型比如:
第三, 更進一步理解:
設計者在設計時AnnotationConfigApplicationContext為什麼是繼承GenericApplicationContext? 因為基於註解的設定,是不太會被執行時修改的,這意味著不需要進行動態Bean設定和重新整理容器,所以只需要GenericApplicationContext。
而基於XML這種組態檔,這種檔案是容易修改的,需要動態性重新整理Bean的支援,所以XML相關的設定必然繼承AbstractRefreshableApplicationContext; 且存在多種xml的載入方式(位置不同的設計),所以必然會設計出AbstractXmlApplicationContext, 其中包含對XML設定解析成BeanDefination的過程。
那麼細心的你從上圖可以發現AnnotationWebConfigApplicationContext卻是繼承了AbstractRefreshableApplicationContext而不是GenericApplicationContext, 為什麼AnnotationWebConfigApplicationContext繼承自AbstractRefreshableApplicationContext呢 ? 因為使用者可以通過ApplicationContextInitializer來設定contextInitializerClasses(context-param / init-param), 在這種情況下使用者傾向於重新整理Bean的,所以設計者選擇讓AnnotationWebConfigApplicationContext繼承了AbstractRefreshableApplicationContext。(如下是原始碼中Spring設計者對它的解釋)
* <p>As an alternative to setting the "contextConfigLocation" parameter, users may
* implement an {@link org.springframework.context.ApplicationContextInitializer
* ApplicationContextInitializer} and set the
* {@linkplain ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM "contextInitializerClasses"}
* context-param / init-param. In such cases, users should favor the {@link #refresh()}
* and {@link #scan(String...)} methods over the {@link #setConfigLocation(String)}
* method, which is primarily for use by {@code ContextLoader}.
我們把之前的設計要點和設計結構結合起來看:
到此,基本可以幫助你從頂層構建對IoC容器的設計理解,而不是過早沉溺於程式碼的細節; 所以《Java全棧知識體系》最大的目標是幫助你構築體系化的認知,如果你自己去看原始碼而不站在頂層設計角度出發, 你多半會撿了芝麻丟了西瓜,時間一長啥印象沒有。@pdai
https://www.cnblogs.com/ITtangtang/p/3978349.html
首先, 從Spring框架的整體架構和組成對整體框架有個認知。
其次,通過案例引出Spring的核心(IoC和AOP),同時對IoC和AOP進行案例使用分析。
基於Spring框架和IOC,AOP的基礎,為構建上層web應用,需要進一步學習SpringMVC。
Spring進階 - IoC,AOP以及SpringMVC的原始碼分析
ConcurrentHashMap<String, Object>
;並且BeanDefinition介面中包含了這個類的Class資訊以及是否是單例等。那麼如何從BeanDefinition中範例化Bean物件呢,這是本文主要研究的內容?