Spring容器基礎XmlBeanFactory(一)

分析下面這行代碼的運行過程:java

BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource(「beanFactoryTest.xml」));

在XMLBeanFactory構造方法XmlBeanFactory(Resource resource):ide

@Deprecated
@SuppressWarnings({"serial", "all"})
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);
    }

}

跟着this.reader.loadBeanDefinitions(resource);進入XmlBeanDefinitionReader類型的reader屬性提供的loadBeanDefinitions(Resource resource)方法.this.reader.loadBeanDefinitions(resource);這行代碼是整個資源加載的切入點,先來看一下調用時序圖: 函數

從上面時序圖中能夠得知,在加載XML文檔和解析註冊Bean,一直在作準備工做.依據上面的時序圖來分析一下這裏究竟在作什麼準備?this

封裝資源文件.當進入XmlBeanDefinitionReader後首先對參數Resource使用EncodedResource類進行封裝
獲取輸入流.從Resource中獲取對應的InputStream並構造InputSource.
經過構造的InputSource實例和Resource實例繼續調用函數doLoadBeanDefinitions
接下來看一下loadBeanDefinitions函數具體的實現過程:編碼

@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    return loadBeanDefinitions(new EncodedResource(resource));
}


其中EncodedResource的做用:對資源文件的編碼進行處理.其中的主要邏輯體如今getReader方法中,當設置了編碼屬性的時候Spring會使用相應的編碼做爲輸入流的編碼.其中getReader方法以下:code

public Reader getReader() throws IOException {
    if (this.charset != null) {
        return new InputStreamReader(this.resource.getInputStream(), this.charset);
    }
    else if (this.encoding != null) {
        return new InputStreamReader(this.resource.getInputStream(), this.encoding);
    }
    else {
        return new InputStreamReader(this.resource.getInputStream());
    }
}


上面的代碼中構造了一個由編碼(encoding)的InputStreamReader.當構造好EncodedResource對象後,再次轉入了可複用方法loadBeanDefinitions(EncodedResource encodedResource).xml

這個方法內部纔是真正的數據準備階段,也就是時序圖所描述的邏輯:對象

public int loadBeanDefinitions(EncodedResource encodedResource)
        throws BeanDefinitionStoreException {
    Assert.notNull(encodedResource, "EncodedResource must not be null");
    if (logger.isInfoEnabled()) {
        logger.info("Loading XML bean definitions from "
                + encodedResource.getResource());
    }

    // 經過屬性來記錄已經加載的資源
    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
    if (currentResources == null) {
        currentResources = new HashSet<EncodedResource>(4);
        this.resourcesCurrentlyBeingLoaded.set(currentResources);
    }
    if (!currentResources.add(encodedResource)) {
        throw new BeanDefinitionStoreException("Detected cyclic loading of "
                + encodedResource + " - check your import definitions!");
    }
    try {
        // 從EncodedResource中獲取已經封裝的Resource對象並再次從Resource中獲取其中的inputStream
        InputStream inputStream = encodedResource.getResource().getInputStream();
        try {
            // InputSource這個類並非來源於Spring,他的全路徑爲:org.xml.sax.InputSource
            InputSource inputSource = new InputSource(inputStream);
            if (encodedResource.getEncoding() != null) {
                inputSource.setEncoding(encodedResource.getEncoding());
            }

            // 真正進入了邏輯核心服務
            return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
        }
        finally {
            inputStream.close();
        }
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException(
                "IOException parsing XML document from "
                        + encodedResource.getResource(), ex);
    }
    finally {
        currentResources.remove(encodedResource);
        if (currentResources.isEmpty()) {
            this.resourcesCurrentlyBeingLoaded.remove();
        }
    }
}


整理一下上面的邏輯:首先對resource參數作封裝,目的是考慮到Resource可能存在編碼要求的狀況,其次,經過SAX讀取XML文件的方式來準備InputSource對象,最後將準備的數據經過參數傳入真正的核心處理部分doLoadBeanDefinitions(inputSource, encodedResource.getResource()).其中doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法實現以下:blog

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
    try {
        Document doc = doLoadDocument(inputSource, resource);
        return registerBeanDefinitions(doc, resource);
    }
    catch (BeanDefinitionStoreException ex) {
        throw ex;
    }
    catch (SAXParseException ex) {
        throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line "
                + ex.getLineNumber() + " in XML document from " + resource
                + " is invalid", ex);
    }
    catch (SAXException ex) {
        throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                "XML document from " + resource + " is invalid", ex);
    }
    catch (ParserConfigurationException ex) {
        throw new BeanDefinitionStoreException(resource.getDescription(),
                "Parser configuration exception parsing XML from " + resource, ex);
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException(resource.getDescription(),
                "IOException parsing XML document from " + resource, ex);
    }
    catch (Throwable ex) {
        throw new BeanDefinitionStoreException(resource.getDescription(),
                "Unexpected exception parsing XML document from " + resource, ex);
    }
}


忽略掉上面的異常處理部分,上面的代碼只作了三件事情:ip

獲取對XML文件的驗證模式 加載XML文件,並獲得對應的Document. 根據返回的Document註冊Bean信息. 上面的三個步驟支撐着整個Spring容器部分的實現基礎.

相關文章
相關標籤/搜索