1. 程式人生 > >Spring原始碼深度解析(四)容器的基礎XmlBeanFactory

Spring原始碼深度解析(四)容器的基礎XmlBeanFactory

現在我們已經對Srping的容器有了一個大概的瞭解,儘管很多地方還很迷糊,但是不要緊,下面我們開始探討每個步驟的詳細實現。接下來我們要深入分析以下程式碼的實現:

BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));

通過XmlBeanFactory初始化時序圖,圖2—7,我們看下上面程式碼的執行邏輯:

時序圖從BeanFactoryTest測試類開始,通過時序圖我們可以一目瞭然地看到整個邏輯處理順序。先呼叫了ClassPathResource的建構函式來構造Resource資原始檔的例項物件,後續的資源處理就可以用Resource提供的各種服務來操作了,當我們有了Resource後就可以進行XmlBeanFactory的初始化了。那麼Resource檔案是如何封裝的呢?

Spring的配置檔案的讀取時通過ClassPathResource進行封裝的,如new ClassPathResource("beanFactoryTest.xml"),那麼ClassPathResource完成了什麼功能呢?

在java中,將不同來源的資源抽象成URL,通過註冊不同的handler(URLStreamHander)來處理不同來源的資源的讀取邏輯,一般handler的型別使用不同的字首(協議,Protocol)來識別,如“file:”,"http:","jar:"等,然而URL沒有預設定義相對Classpath或ServletContext等資源的handler,雖然可以註冊自己的URLStreamHandler來解析特定的URL字首(協議),比如“classpath:”,然而這需要了解URL的實現機制,而且URL也沒有提供一些基本的方法,如檢查當前資源是否存在,檢查當前資源是否可讀等方法。因而Spring對其內部使用到的資源實現了自己的抽象結構:Resource介面來封裝底層資源。

這兩個類都是屬於Spring-core包中的

InputStreamSource封裝任何能返回InputStream的類,比如File,Classpath下的資源和Byte Array等。它只有一個方法定義:getInputStream(),該方法返回一個新的InputStream物件。

Resource介面抽象了所有Spring內部使用到的底層資源:File,URL,Classpath等。首先,它定義了3個判斷當前資源狀態的方法:存在性(exists),可讀性(isReadable),是否處於開啟狀態(isOpen).另外,Resource介面還提供了不同資源到URL,URI,File型別的轉換,以及獲取lastModified屬性,檔名(不帶檔案資訊的檔名,getFilename())的方法。為了便於操作,Resource還提供了基於當前資源建立一個相對資源的方法:createRelative().在錯誤處理中需要詳細地打印出錯的資原始檔,因而Resource還提供了getDescription()方法用於在錯誤處理中的列印資訊。

對不同來源的資原始檔都有相應的Resource實現:檔案(FileSystemResource),Classpath資源(ClasspathResource),URL資源(URLResource),InputStream資源(InputStreamResource),Byte陣列(ByteArrayResource)等。相關類圖如2-8所示。

在日常的開發工作中,資原始檔的載入也是經常用到的,可以直接使用Spring提供的類,比如在希望載入檔案時可以使用以下程式碼:

Resource resource = new ClassPathResource("beanFactoryTest.xml");

InputStream inputStream = resource.getInputStream();

得到inputStream後,我們可以按照以前的開發方式進行實現了,並且我們已經可以利用Resource及其子類為我們提供好的諸多特性。

有了Resource介面便可以對所有資原始檔進行統一處理。至於實現,其實是非常簡單的,以getInputStream為例,ClassPathResource中的實現方式便是通過class或者classLoader提供的底層方法進行呼叫,而對於FileSystemResource的實現其實更簡單,直接使用FileInputStream對檔案進行例項化。

ClassPathResource.java:

FileSystemResource.java:

當通過Resource相關類完成了對配置檔案進行封裝後配置檔案的讀取工作就全權交給XmlBeanDefinitionReader來處理了。

瞭解了Spring中將配置檔案封裝為Resource型別的例項方法後,我們就可以繼續探討XmlBeanFactory的初始化方法了,XmlBeanFactory初始化有許多方法,Spring中提供了很多的建構函式,在這裡分析的是使用Resource例項作為建構函式引數的方法,程式碼如下:

上面函式的程式碼中,this.reader.loadBeanDefinitions(resource)才是資源載入的真正實現,也是我們分析的重點之一。我們可以看到時序圖中提到的XmlBeanDefinitionReader載入資料就是這裡完成的,但是在XmlBeanDefinitionReader載入資料前還有一個呼叫父類建構函式初始化的過程:super(parentBeanFactory),跟蹤程式碼到父類AbstractAutowireCapableBeanfactory的建構函式中:

AbstractAutowireCapableBeanFactory.java:

這裡有必要提及下ignoreDependencyInterface方法。ignoreDependencyInterface的主要功能,是忽略給定介面的自動裝配功能,那麼,這樣做的目的是什麼呢?會產生什麼樣的效果呢?

舉例來說,如果A中有B屬性,Spring在獲取A的時候會自動初始化B,但某些情況下B不需要被初始化,有種情況就是B實現了BeanNameAware介面。Spring中是這樣介紹的,自動裝配的時候,忽略給定的依賴介面,典型的應用是通過其他方式解析Application上下文註冊依賴,類似於BeanFactory通過BeanFactoryAware進行注入或者ApplicationContext通過ApplicationContextAware進行注入。

內容有點多,下面一節會講解XmlBeanFactory如何通過XmlBeanDefinitionReader進行載入Bean