Spring源碼分析-IOC容器BeanFactory的設計原理

BeanFactory介面提供了使用IOC容器的規範。在這個基礎上,Spring提供了符合了這個IOC容器介面的一系列的實現供開發人員使用。我們以XmlBeanFactory的實現為例來說明簡單IOC容器的設計原理。如圖1所示為XmlBeanFactory設計的繼承關係。

圖1

可以看到,作為一個簡單IOC容器系列最底層實現的XmlBeanFactory,與我們在Spring應用中用到的那些上下文對比,有一個非常明顯的特點;它只是提供最基本的IOC容器的功能。理解這一點有助於我們理解ApplicationContext與基本BeanFactory之間的去吧和聯繫。我們可以認為直接的BeanFactory實現是IOC容器的基本形式,而各種ApplicationContext的實現是IOC容器的高級表現形式。關於ApplicationContext的分析,以及它與BeanFactory相比的增強性都會在下面進行詳細的分析。

Advertisements

我們好好地看下圖1的繼承關係,從中可以清楚地看到類之間的聯繫,它們都是IOC容器系列的組成部分。在設計這個容器系列時,我們可以從繼承體系的發展上看到IOC容器各項功能的實現過程。如果要擴展自己的容器產品,建議讀者最好在繼承體系中檢查一下,看看Spring是不是已經提供了現成的或相近的容器實現供我們參考。下面就從我們比較熟悉的XmlBeanFactory的實現入手進行分析,來看看一個基本的IOC容器是怎樣實現的。

仔細閱讀XmlBeanFactory的源碼,在一開始的註釋里會看到對XmlBeanFactory功能的簡要說明,從代碼的註釋還可以看到,這是Rod Johnson在2001年就寫下的代碼,可見這個類應該是Spring的元老類了。XmlBeanFactory繼承自DefaultListableBeanFactory這個類,後者非常重要,是我們經常要用到的一個IOC容器實現,比如在設計應用上下文ApplicationContext時就會用到它。我們會看到這DefaultListableBeanFactory實際上包含了基本IOC容器所具有的重要功能,也是在很多地方都會用到的容器系列中的一個基本產品。

Advertisements

在Spring,實際上是把DefaultListableBeanFactory作為一個默認的功能完整的IOC容器來使用的。XmlBeanFactory在繼承了DefaultListableBeanFactory容器的功能的同時,增加了新的功能,這些功能很容易從XmlBeanFactory的名字上猜到。它是一個XML相關的BeanFactory,也就是說它是一個可以讀取XML文件方式定義的BeanDefinition的IOC容器。

這些實現XML讀取的功能是怎樣實現的呢?對這些XML文件定義信息的處理並不是由XmlBeanFactory直接完成的。在XmlBeanFactory中,初始化了一個XmlBeanDefinitionReader對象,有了這個Reader對象,那些以XML方式定義的BeanDefinition就有了處理的地方。我們可以看到,對這些XML形式的信息的處理實際上是由這個XmlBeanDefinitionReader來完成的。

構造XmlBeanFactory這個IOC容器時,需要指定BeanDefinition的信息來源,而這個信息來源需要封裝成Spring中的Resources類來給出。Resources是Spring用來封裝I/O操作的類。比如,我們的BeanDefinition信息是以XML文件形式存在的,那麼可以使用像「ClassPathResources=new ClassPathResource(「beans.xml」);」這樣具體的ClassPathResource來構造需要的Resource,然後將Resource作為構造參數傳遞給XmlBeanFactory構造函數。這樣IOC容器就可以方便地定位到需要地BeanDefinition信息來對Bean完成容器地初始化和依賴注入過程。

XmlBeanFactory地功能是建立在DefaultListableBeanFactory這個基本容器地基礎上,並在這個基本容器地基礎上實現了其他諸如XML讀取地附加功能。對於這些功能地實現原理,看一看XmlBeanFactory地代碼實現就能很容易地理解。代碼清單如下面清單所示,在XmlBeanFactory構造方法中需要得到Resource對象。對XmlBeanDefinitionReader對象地初始化,以及使用這個對象來完成對loadBeanDefinitions的調用,就是這個調用啟動從Resource中載入BeanDefinitions的過程,LoadBeanDefinitions同時也是IOC容器初始化的重要組成部分。

public class XmlBeanFactory extends DefaultListableBeanFactory {

private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);

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作為基類,DefaultListableBeanFactory是很重要的一個IOC實現,在其他IOC容器中,比如ApplicationContext,其實現的基本原理和XmlBeanFactory一樣,也是通過持有或者擴展DefaultListableBeanFactory來獲得基本的IOC容器的功能的。

參考XmlBeanFactory的實現,我們以編程的方式使用DefaultListableBeanFactory。從中我們可以看到IOC容器使用的一些基本過程。儘管我們在應用中使用IOC容器時很少會使用這樣原始的方式,但是了解一下這個基本過程,對我們了解IOC容器的工作原理是非常有幫助的。因為這個編程式使用容器的過程,很清楚揭示了在IOC容器實現中的那些關鍵的類(比如Resource,DefaultListableBeanFactory和BeanDefinitionReader)之間的相互關係,例如他們是如何把IOC容器的功能解耦的、又是如何結合在一起為IOC容器服務的、等等。在如下代碼清單可以看到編程式使用IOC容器的過程。

ClassPathResource res=new ClassPathResource("beans.xml");

DefaultListableBeanFactory factory=new DefaultListableBeanFactory();

XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(factory);

reader.loadBeanDefinitions(res);

這樣,我們就可以通過factory對象來使用DefaultListableBeanFactory這個IOC容器了。在使用IOC容器時,需要如下幾個步驟:

1)創建IOC配置文件的抽象資源,這個抽象資源包含了BeanDefinition的定義信息。

2)創建一個BeanFactory,這裡使用DefaultListableBeanFactory。

3)創建一個載入BeanDefinition的讀取器,這裡使用XmlBeanDefinitionReader來載入XML文件形式的BeanDefinition,通過一個回調配置給BeanFactory。

4)從定義好的資源位置讀入配置信息,具體的解析過程有XmlBeanDefinitionReader來完成。完成整個載入和註冊Bean定義之後,需要的IOC容器就建立起來了。這個時候就可以直接使用IOC容器了。

Advertisements

你可能會喜歡