spring源碼(1)從xml到org.w3c.dom.Document

從以下這一小段代碼說起:

new XmlBeanFactory(new ClassPathResource("springContext.xml"));

這小段看似簡單僅僅實例化了兩個對象,但是這只是表象。

一、XmlBeanFactory的構造函數

查看代碼發現XmlBeanFactory有兩個構造函數:

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);
}

最終會執行的代碼是:

//執行父類帶參構造函數
super(parentBeanFactory);
//執行XmlBeanDefinitionReader的loadBeanDefinitions
this.reader.loadBeanDefinitions(resource);

可以看到this.reader.loadBeanDefinitions纔是資源加載的真正實現。XmlBeanFactory相當於 DefaultListableBeanFactory + XmlBeanDefinitionReader,既可以對bean進行一些操作,又可以讀取XML。

Resource用來封裝配置文件。

二、XmlBeanDefinitionReader讀取XML

一路走下去,方法到了doLoadBeanDefinitions方法,開始解析XML文件的地方(做了些簡化):

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
       ...

        Document doc = doLoadDocument(inputSource, resource);
        return registerBeanDefinitions(doc, resource);
       ...
}

進入doLoadDocument(inputSource, resource),實際上是使用配置的documentLoader來解析XML的:

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
    return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
            getValidationModeForResource(resource), isNamespaceAware());
}

常見的XML解析方式有:DOM、SAX、dom4j等,那麼spring中使用的是哪種方式呢?
spring中採用的是DOM方式,所要做的一切就是得到org.w3c.dom.Document。
loadDocment 傳遞的參數有:
① EntityResolver : 指定XML驗證方式,實現先從本地獲取XSD或者DTD文件
② errorHandler : 解析錯誤處理類
③ ValidationMode : 驗證方式XSD、DTD
④ isNamespaceAware :是否支持命名空間

EntityResolver、errorHandler參考:http://blog.csdn.net/disiwei1012/article/details/75209030

一、驗證模式介紹

ValidationMode:比較常用的驗證方式有兩種:DTD和XSD

DTD模式:
要使用DTD進行驗證,需要在XML文件的頭部聲明,同時需要制定DTD文件的位置。
spring中使用DTD如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC  "-//SPRING//DTD BEAN 2.0//EN"  "http://www.springframework.org/dtd/spring-beans-2.0.dtd">

XML的DOCTYPE有兩個屬性publicId和systemId,這兩個東西都可以用來制定DTD文件的位置。所不同的是systemId指定的是具體的資源文件,它可以是相對路徑也可以是絕對路徑,而publicId則是使用定義的名稱查找資源文件

publicId 是一個公共資源的知名標識,解析器可以根據這個名字得到一個資源(一般是DTD),如果根據這個名字沒能找到一個資源,那麼會根據systemId(一般是一個URL)來獲取這個資源,如果還獲取不到,那麼會報錯。也就是說,publicId和systemId是一個網絡資源的標識,前者標識名字,後者標識URL,一般是成對出現。

解析器獲取publicId標識的資源,一般是通過網絡下載,這會導致解析速度變慢,或者解析失敗。因此,可以在解析前編寫驗證文件,或者制定驗證文件的地址。

上面的:
publicId:-//SPRING//DTD BEAN 2.0//EN
systemId:http://www.springframework.org/dtd/spring-beans-2.0.dtd

XSD模式:
使用XSD驗證,除了聲明命名空間外xmlns,還要通過schemaLocation來指定該命名空間驗證文件的位置。

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans> -->

publicId:null
systemId:http://www.springframework.org/schema/beans/spring-beans.xsd

二、spring中對xml的驗證

接着往下走:

public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
            ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

        DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
        if (logger.isDebugEnabled()) {
            logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
        }
        DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
        return builder.parse(inputSource);
    }

和咱們想象的差不多,先創建DocumentBuilderFactory,然後DocumentBuilder,然後parse。

protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
            throws ParserConfigurationException {
        //創建DocumentBuilderFactory
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        //是否支持命名空間
        factory.setNamespaceAware(namespaceAware);

        //如果需要進行xml驗證,而且是XSD驗證,強制支持命名空間
        if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
            factory.setValidating(true);
            if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
                // Enforce namespace aware for XSD...
                factory.setNamespaceAware(true);
                try {
                    factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
                }
                catch (IllegalArgumentException ex) {
                    ParserConfigurationException pcex = new ParserConfigurationException(
                            "Unable to validate using XSD: Your JAXP provider [" + factory +
                            "] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
                            "Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
                    pcex.initCause(ex);
                    throw pcex;
                }
            }
        }

        return factory;
    }

//沒啥需要解釋的
protected DocumentBuilder createDocumentBuilder(
            DocumentBuilderFactory factory, EntityResolver entityResolver, ErrorHandler errorHandler)
            throws ParserConfigurationException {

        DocumentBuilder docBuilder = factory.newDocumentBuilder();
        if (entityResolver != null) {
            docBuilder.setEntityResolver(entityResolver);
        }
        if (errorHandler != null) {
            docBuilder.setErrorHandler(errorHandler);
        }
        return docBuilder;
    }

三、另外看下spring的entityResolver

如果沒有特備指定entityResolver,則使用默認的entityResolver。
getResourceLoader是XmlBeanDefinitionReader父類AbstractBeanDefinitionReader中的方法,查看後發現resourceLoader == null。
所以真正使用的是DelegatingEntityResolver。

protected EntityResolver getEntityResolver() {
        if (this.entityResolver == null) {
            // Determine default EntityResolver to use.
            ResourceLoader resourceLoader = getResourceLoader();
            if (resourceLoader != null) {
                this.entityResolver = new ResourceEntityResolver(resourceLoader);
            }
            else {
                this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
            }
        }
        return this.entityResolver;
    }

下面在DelegatingEntityResolver:

public DelegatingEntityResolver(ClassLoader classLoader) {
        //DTD驗證EntityResolver
        this.dtdResolver = new BeansDtdResolver();
        //XSD驗證EntityResolver
        this.schemaResolver = new PluggableSchemaResolver(classLoader);
    }

在EntityResolver指定本地驗證文件的位置,如果找不到,再通過systemId的位置去找。

以上就得到了org.w3c.dom.Document,不算太曲折!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章