1. 程式人生 > >Spring IOC-XmlBeanFactory如何載入xml及如何儲存轉換後的資訊

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定義的是不是單例的,如果是單例的那麼就會例項化,如果不是單例的,是發生在使用者第一次請求的時候例項化。