Spring IOC-XmlBeanFactory如何載入xml及如何儲存轉換後的資訊
本文主要介紹我們定義的xml配置檔案是怎樣被Spring載入封裝到bean工廠的。
我們寫程式碼使用Spring的IOC通常是這樣的:
Resource resource=new FileSystemResource("benas-config.xml");
BeanFactory factory=new XmlBeanFactory(resource);
HelloBean hello=(HelloBean)factory.getBean("helloBean");
System.out.println(hello.getHelloWord());
配置檔案通常是這樣的
<bean id="helloBean" class="spring2.HelloBean">
<property name="helloWord">
<value>hello</value>
</property>
</bean></beans>
那麼我們就直接看底層程式碼是怎麼實現的,轉到XmlBeanFactory中,在這個類構造的時候就會去讀取我們傳入的xml檔案,像這樣:
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
那麼我們自然轉到XmlBeanDefinitionReader類中的實現程式碼:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
try {
//轉換為輸入流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//構造InputSource
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//把xml中的bean的定義解析出來
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
轉到doLoadBeanDefinitions(InputSource inputSource, Resource resource)中,主要程式碼如下:
………………
try {
int validationMode = getValidationModeForResource(resource);
//呼叫[com.sun.org.apache.xerces.internal
//.jaxp.DocumentBuilderImpl.parse(inputSource)]解析為Document物件
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
return registerBeanDefinitions(doc, resource);
}
………………
在轉到registerBeanDefinitions方法:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
documentReader.setEnvironment(this.getEnvironment());
int countBefore = getRegistry().getBeanDefinitionCount();
//註冊當前解析到的bean到工廠,注意這裡會初始化“讀環境”,初始化換進過的時候會將當前的beanFactory的物件引用傳入,這樣以後這個環境裡就維持了一個到工廠的引用
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));//在這裡報錯
return getRegistry().getBeanDefinitionCount() - countBefore;//DefaultBeanDefinitionDocumentReader
}
注意這裡的BeanDefinitionDocumentReader物件實際為DefaultBeanDefinitionDocumentReader(作用完成bean的定義讀取和封裝到bean工廠),然後看這個類:
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
protected void doRegisterBeanDefinitions(Element root) {
preProcessXml(root);
//實際的解析過程
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
}
}
然後我們在把程式碼定位到parseBeanDefinitions方法,因為要貼的程式碼有點多,這裡就截圖了:
圖上註釋說的很清楚,這個解析的過程是用Spring定義的一套標籤的解析類來完成的,每一個標籤都對應自己的解析類,這裡要注意幾個特殊的標籤,是沒有解析類的,它們是alias、name、import和bean,對於這四個標籤,Spring直接呼叫在DefaultBeanDefinitionDocumentReader中的方法解析。
具體解析過程我們以標籤為例說明(最終的解析封裝過程在BeanDefinitionParserDelegate類中)。
我們看到一些列的解析,最終構造出了AbstractBeanDefinition類,就是說bean工廠在讀取配置檔案之後是轉換為了一系列的AbstractBeanDefinition類,然後在工廠會判斷一個bean定義的是不是單例的,如果是單例的那麼就會例項化,如果不是單例的,是發生在使用者第一次請求的時候例項化。