您好,登錄后才能下訂單哦!
本篇內容主要講解“Spring容器BeanFactory怎么使用”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Spring容器BeanFactory怎么使用”吧!
Spring容器是Spring的核心,它可以創(chuàng)建對象,把他們關聯(lián)在一起,配置各個對象,并管理每個對象的整個生命周期。Spring容器使用依賴注入(DI)來管理組成一個應用程序的組件。這些對象被稱為Spring Beans (一個對象就是一個Bean)。
Spring中有兩種容器:
① BeanFactory 一個最簡單的Spring容器,給依賴注入(DI)提供了基礎的支持。
② ApplicationContext 此容器添加以一些企業(yè)需要用到的東西,更加全面。它包含了BeanFactory容器中的東西。
在Spring中,有大量BeanFactory接口的實現(xiàn)類(見下圖),但是,最常用的也就是XmlBeanFactory類(在Eclipse中,查看其源碼可以看見已經(jīng)是一個過時的類了,但我們也需要了解。),它可以從一個 XML 文件中讀取配置元數(shù)據(jù),由這些元數(shù)據(jù)來生成一個被配置化的系統(tǒng)或者應用。
(BeanFactory接口實現(xiàn)類)
本文主要是針對下面一行代碼執(zhí)行所發(fā)生的事情的一些深入探究。
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("配置文件"));
Ⅰ. DefaultListableBeanFactory
XmlBeanFactory類繼承自DefaultListableBeanFacotry類,而DefaultListableBeanFactory類是Bean加載的核心部分,是Spring注冊及加載Bean的默認實現(xiàn)。
XmlBeanFactory類與DefaultListableBeanFactory類之間不同的地方就在于XmlBeanFactory類中使用了自定義的XML讀取器XmlBeanDefinitionReader,
(XmlBeanDefinitionReader對象)
實現(xiàn)了個性化的BeanDefinitionReader讀取,DefaultListableBeaFactory類繼承了AbstractAutoWireCapableBeanFactory類,并實現(xiàn)了ConfigurableListableBeanFactory以及BeanDefinitionRegistry接口。
(DefaultListableBeanFactory類)
DefaultListableBeanFactory類的基類,實現(xiàn)接口的一些相關類圖:
(容器加載部分相關類圖)
XmlBeanFactory類就繼承自DefaultListableBeanFactory類。XmlBeanFactory類對DefaultListableBeanFactory類進行了擴展,在XMLBeanFactory中主要使用reader屬性對資源文件進行讀取和注冊。
XmlBeanFactory類的構造方法如下圖:
(XmlBeanFactory構造方法)
Ⅱ. XmlBeanDefinitionReader
上面我們已經(jīng)知道了XmlBeanFactory類和DefaultListableBeanFactory類的區(qū)別了,XmlBeanFactory類中定義了一個XmlBeanDefinitionReader對象,用于對資源文件進行處理。
XML配置文件的讀取對于Spring而言非常重要,因為Spring絕大部分功能都是以配置文件作為切入點的,那么,我們就要從XmlBeanDefinitionReader類中梳理一下資源文件讀取、解析和注冊的大致流程。
(配置文件讀取相關類)
通過上面配置文件讀取相關類圖可以得到讀取大致流程如下:
① 通過繼承自 AbstratcBeanDefinitionReader 中的方法,來使用 ResourceLoader 將資源文件路徑轉換為對應的 Resource 文件。
② 通過 DocumentLoader 對 Resource 文件進行轉換,將 Resource 文件轉換為 Document 文件。
③ 通過 DefaultBeanDefinitionDocumentReader 類對Document進行解析,并使用 BeanDefinitionParserDelegate 對 Element 進行解析。
上述三步只是讀取配置文件的一個大致流程,接下來將進行更加詳細的解析。
上面我們已經(jīng)知道了 XmlBeanFacotry 和 DefaultListableBeanFactory 的區(qū)別了。XmlBeanFactory 結構如下圖:
(XmlBeanFactory類結構)
在Spring中,我們創(chuàng)建了一個配置文件,并且在配置文件中配置了一個Bean后,那么,我們就要獲取這個Bean。在測試代碼中我們都寫過這樣一句代碼:
XmlBeanFactory xmlBeanFactory = new XmlBeanFacotry(new ClassPathResource("配置文件"));
也可以是:
BeanFactory beanFactory = new XmlBeanFacotry(new ClassPathResource("配置文件"));
總之,以上兩句行代碼就是讀取配置文件創(chuàng)建容器。
那么,new XmlBeanFacotry(new ClassPathResource("配置文件")) 這句代碼到底是怎么回事呢?先看看下面的 XmlBeanFactory 初始化時序圖吧!(畫得不好,將就看吧)
(XmlBeanFactory初始化時序圖)
時序圖解析:
◆ 首先,時序圖從一個測試類開始,這個測試類就是上面創(chuàng)建 XmlBeanFactory 那里。
◆ 創(chuàng)建 XmlBeanFactory 需要一個 Resource 對象,由于 Resource 是接口,所以我們使用其實現(xiàn)類 ClassPathResource 來構造 Resource 資源文件的實例對象。
◆ 有了 Resource 對象就可以進行 XmlBeanFactory 的初始化了,最后得到一個 BeanFactory。
那么,問題來了,什么又是 Resource呢?它是怎么對資源進行封裝處理的呢?
● Resource 是什么
在說 Resource 之前,我們要知道一個接口:InputStreamSource,該接口封裝任何能返回 InputStream 的類,比如File、Classpath下的資源、Byte、Array等。它只定義了一個方法:InputStream getInputStream() throws IOException; 該方法返回一個 InputStream 對象。
Resource 接口抽象了所有 Spring 內部所使用到的底層資源: File、URL、Classpath等。Resource 接口中的方法及大致作用如下圖:
(Resource接口)
對于不同來源的資源文件都有對象的 Resource 實現(xiàn):
文件(FileSystemResource)、Classpath資源(ClassPathResource)、URL資源(UrlResource)等。
(資源文件處理部分相關類)
資源文件的加載在日常開發(fā)中也經(jīng)常被使用,可以直接使用 Spring 提供的類,如在加載文件時使用如下代碼:
Resource resource = new ClassPathResource("資源文件");
InputStream inputStream = resource.getInputStream();
得到 inputStream 后,我們就可以按照以往的開發(fā)方式進行開發(fā)了,而且還可以使用 Resource 及其實現(xiàn)類的一些東西。
當通過 Resource 相關類完成了對配置文件的封裝后,配置文件的讀取就是 XmlBeanDefinitionReader 來完成了。
現(xiàn)在我們已經(jīng)知道了 Spring 中將配置文件封裝為 Resource 對象,下面繼續(xù)了解 XmlBeanFactory 的初始化過程。從上面 XmlBeanFactory類結構圖中可以看出 XMLBeanFactory 類共有兩個構造方法,如下圖:
(XmlBeanFactory類構造方法)
從上圖我們看出,第一個構造方法內部調用了該類內部的另一個構造方法。所以,我們也就了解第二個構造方法了。
首先,在第一行出現(xiàn)了 super(parentBeanFactory) 這樣一句代碼,調用了父類(DefaultListableBeanFactory)的一個構造方法。
(DefaultListableBeanFactory有參構造方法)
來到父類構造方法,我們發(fā)現(xiàn)又繼續(xù)調用了父類(AbstractAutowireCapableBeanFactory)的構造方法,見下圖:
(AbstractAutowireCapableBeanFactory類構造方法)
從圖中可以看出,有參構造方法(我們使用的就是有參構造)先調用了本類中的一個無參構造方法,無參構造方法首先執(zhí)行了父類(AbstractBeanFactory)的構造方法(一個空方法),這里了解一下 ignoreDependencyInterface 方法,該方法的主要功能就是 忽略自動連接給定的依賴接口(忽略給定接口的自動裝配功能)。那么,該方法有什么用?
如:當 A類 中有屬性 B,當 Spring 在獲取 A 的 Bean 的時候如果屬性 B 還沒有被初始化,Spring 就會自動初始化 B,(這也是Spring的一個重要特性)。但是,某些情況下,B 不會被初始化,比如 B 實現(xiàn)了 BeanNameAware 接口。Spring API介紹:應用程序上下文通常使用它來注冊以其他方式解析的依賴項,如通過BeanFactoryAware實現(xiàn)的BeanFactory或通過ApplicationContextAware實現(xiàn)的ApplicationContext。默認情況下,只忽略BeanFactoryAware接口。若要忽略其他類型,請為每個類型調用此方法。
最后調用 setParentBeanFactory 方法設置 BeanFactory對象。
(setParentBeanFactory方法)
在 setParentBeanFactory 方法中有一個 if 判斷,用于判斷是否已經(jīng)關聯(lián)了 BeanFactory,如果已經(jīng)關聯(lián)就拋出異常。
? 加載Bean
在上面講到 Resource 時,我們知道了 XmlBeanFactory 的構造方法,我們也知道了其中一個構造方法首先調用了父類的構造方法,那么,在super()語句下面就是 this.reader.loadBeanDefinitions(resource) 方法的調用。這個方法才是整個資源加載的切入點,下面是該方法調用的時序圖:
(loadBeanDefinitions方法執(zhí)行時序圖)
從上圖可以看到,這個方法的調用引起了很大一串的工作。然而這些工作也只是在做準備工作,下面說說這里究竟在準備什么工作:
(1)封裝資源文件。當調用 loadBeanDefinitions 方法時,就會跳轉到該方法中,該方法就調用了本類的一個重載方法,同時根據(jù) Resource 對象創(chuàng)建一個EncodedResource 對象作為參數(shù)傳遞,使用 EncodedResource 的作用就是把 Resource 使用 EncodedResource 類進行封裝。
(loadBeanDefinitions(Resource resource)方法)
(2)獲取輸入流構建 inputSource。從 Resource 中獲取對應的 InputStream 并創(chuàng)建 InputSource。
(loadBeanDefinitions(EncodedResource encodedResource)方法中 獲取 InputStream 并 創(chuàng)建 InputSource)
(3)通過剛剛創(chuàng)建的 InputSource 對象和 Resource繼續(xù)調用 doLoadBeanDefinitions()方法。
(doLoadBeanDefinitions(InputSource, Resource)方法調用)
上面,我們多次看到 EncodedResource ,那么,它到底是什么?
? EncodedResource
通過名字可以猜測該類和編碼相關。該類主要就是對資源文件的編碼進行處理的。其中一個很重要的方法 getReader() 方法 ,當設置了編碼屬性時,Spring 就會使用相應的編碼作為輸入流的編碼。
首先看看它的一個構造方法:(該類一共有四個構造方法,但其余三個構造方法均調用了下面這個構造方法)
(EncodedResource類的一個構造方法)
該構造方法主要就是對類中的屬性進行初始化。
再來看看 getReader() 方法:
(getReader() 方法)
getReader() 方法構造了一個含編碼的 InputStreamReader。將 Resource 封裝為 EncodedResource 對象后,就來到了 XmlBeanDefinitionReader 類中的 loadBeanDefinitions() 方法(另一個重載后的方法),也就是下圖中的方法。
(loadBeanDefinitions(EncodedResource ..)方法部分重要代碼)
上圖方法才算是真正的數(shù)據(jù)準備,也就是往上第7張 時序圖中所描述的部分。
再次回顧以上數(shù)據(jù)準備部分內容,首先將傳入的 Resource 對象封裝為 EncodedResource,為什么需要封裝?目的是考慮到 Resource 可能存在編碼要求的情況,其次,通過SAX讀取XML文件的方式來準備 InputSource對象,最后將準備的數(shù)據(jù)通過參數(shù)傳遞給核心處理方法 doLoadBeanDefinitions(InputSource inputSource, Resource resource)。下面就來看看 doLoadBeanDefinitions() 方法做了什么事情:
(doLoadBeanDefinitions() 方法部分代碼(除catch部分))
doLoadBeanDefinitions() 方法 try 后面有多個 catch ,除開這些 catch,那么,這段代碼做了以下三件事情:
(1)獲取對 XML 文件的驗證模式
(2)加載 XML 文件,得到對應的 Document 對象
(3)根據(jù)返回的 Document 對象注冊 Bean 信息
以上三個操作支撐著整個Spring容器的實現(xiàn)基礎,下面就將從這三個步驟講起。
? 獲取 XML的驗證模式
XML 驗證模式的作用:用于保證 XML 文件的正確性(貌似就是所說的約束),常用的驗證模式有兩種:DTD(Document Type Definition) 和 XSD(XML Schemas Definition)。詳細了解驗證模式請自行上網(wǎng)搜索。
在上一張(doLoadBeanDefinitions方法代碼)圖中,我們看見 try 塊中第一行代碼是: Document document = doLoadDocument(inputSource, resource); 調用了本類中的 doLoadDocument() 方法,但是,這句代碼主要是用來獲取 Document對象的,也就是上面第二件事情;但是,在其中會先完成對 XML 文件驗證模式的獲取。
(doLoadDocument() 方法)
我們可以從上圖看到,在 loadDocument() 方法執(zhí)行時,先執(zhí)行了 getValidationModeForResource(resource) 方法,該方法返回一個 int 類型的值。(在 Spring3.2 中,并不存在 doLoadDocument() 方法,是直接在 doLoadBeanDefintions() 方法中調用 getValidationModeForResource(resource) 方法 和 loadDocument() 方法),下面我們看看 getValidationModeForResource(resource) 方法做了什么事情:
(getValidationModeForResource(resource) 方法)
在上面方法中,使用了幾個常量,下圖是幾個常量所表示的值:
(org.springframework.util.xml.XmlValidationModeDetector類中的幾個常量)
注意:XmlBeanDefinitionsReader 類中也有上圖中除了 DOCTYPE 常量以外的幾個 int 類型的同名的常量,其值就是上面的值。也就是 Spring 把不同的驗證模式使用了不同數(shù)值表示了而已。
在往上第二張圖中得知 getValidationModeForResource(resource) 方法首先判斷是否手動指定(通過 setValidationMode() 方法設置驗證模式)了驗證模式,判斷方式就是 獲取 validationMode 屬性進行判斷。否則使用自動檢測的方式,自動檢測調用了 org.springframework.beans.factory.xml.XmlBeanDefinitionReader.detectValidationMode(resource) 方法(本類中的方法)實現(xiàn)。
(XmlBeanDefinitionReader.detectValidationMode(resource) 方法 省略了catch處理部分代碼)
在上圖最后一個 try 塊調用了 XmlValidationModeDetector 類中的 detectValidationMode(inputStream) 方法做進一步處理,下面就看看這個方法(這里如果要全面了解,建議自己查看一遍源碼,畢竟在該方法中還調用了其他方法,也使用了幾個常量,這里并沒有列出)。
(XmlValidationModeDetector 類中的 detectValidationMode(inputStream) 方法)
上面獲取驗證模式部分需要根據(jù) DTD 和 XSD來進行理解,因為獲取驗證模式就是根據(jù)兩種驗證模式使用方法來的。Spring 檢測驗證模式的方法就是判斷是否包含 DOCTYPE,如果包含就是 DTD,否則就是 XSD(這一點從上面一張圖的第一行注釋就是可以看出:查看文件以查找 DOCTYPE),這一點從上圖方法中可以很容易的看出。
到這里,獲取驗證模式就講解完了。
? 獲取 Document
上面我們知道了在獲取 Document 之前要先獲取 XML 驗證模式。下面我們就來看看 Spring 中是怎么獲取 Document 的。在上面 (doLoadDocument() 方法)圖中我們看見 doLoadDocument 方法調用了本類 documentLoader 的 loadDocuemnt() 方法。documentLoader 定義如下:
private DocumentLoader documentLoader = new DefaultDocumentLoader();
DocumentLoader 是一個接口,所以使用其實現(xiàn)類 DefaultDocumentLoader;
先來看看 DefaultDocumentLoader 類中的 loadDocument() 方法吧。
(loadDocument() 方法)
上面這段代碼就是基本的 通過 SAX 解析 XML,這里算是基本步驟了。首先創(chuàng)建 DocumentBuilderFacotry 對象,再通過 DocumentBuilderFactory 創(chuàng)建 DocumentBuilder,然后解析 inputSource 來返回 Document 對象。這里涉及到了 XML 解析相關知識,可自行上網(wǎng)深入了解。
? 解析及注冊 BeanDefinitions
在上面 (doLoadBeanDefinitions() 方法) 圖中我們知道 獲取到了 Document 后,就執(zhí)行下面這行代碼:
return registerBeanDefinitions(doc, resource);
也就是 繼續(xù)調用 registerBeanDefinitions(doc, resource) 方法。
(registerBeanDefinitions(doc, resource) 方法)
上圖中第一行代碼就是創(chuàng)建 BeanDefinitionDocumentReader,BeanDefinitionDocumentReader 是接口,而實例化是在 createBeanDefinitionDocumentReader() 方法中完成的,而通過執(zhí)行此方法后,BeanDefinitionDocumentReader 真正的類型就是 DefaultBeanDefinitionDocumentReader (它的實現(xiàn)類)了。
上圖中第三行就是加載、注冊 Bean了,由于 BeanDefinitionDocumentReader 是接口,所以我們來到 DefaultBeanDefinitionDocumentReader 類中的 registerBeanDefinitions() 方法。
(registerBeanDefinitions(Document, XmlReaderContext) 方法)
上圖方法的重要目的之一就是提取 root,再將 root 作為參數(shù)繼續(xù) BeanDefinition 的注冊。
?。╠oRegisterBeanDefinitions(Element) 方法)
上圖代碼中涉及到 profile 屬性,該屬性詳情還請自行上網(wǎng)了解。上圖程序第二部分,程序會先獲取 beans 節(jié)點是否定義了 profile 屬性,如果定義了則需要到環(huán)境變量中區(qū)尋找,每定義就不解析。
處理了 profile 就可以開始進行 XML的讀取了,下面看看上圖框住的方法 parseBeanDefinitions(root, this.delegate)。
(parseBeanDefinitions(Element, BeanDefinitionParserDelegate) 方法)
在 Spring 的 XML文件中可以使用默認的 Bean聲明,也可以自定義。所以 Spring 針對不同的 Bean 聲明做了不同的處理。
到此,相信大家對“Spring容器BeanFactory怎么使用”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續(xù)學習!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。