(一)Spring IoC源码-1.容器的初始化-03获取Document实例

在上篇文章IoC容器源码-容器的基础的末尾,我们讲到了“加载Xml文件,并封装成Document 实例”是通过Document doc = doLoadDocument(inputSource, resource);来完成的。本文将详细讲解doLoadDocument方法是如何获取Document的。

org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadDocument(InputSource, Resource)

/**
 * Actually load the specified document using the configured DocumentLoader.
 * ...
 */
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
    return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
            getValidationModeForResource(resource), isNamespaceAware());
}

可以看到,获取Document实例是依赖documentLoader.loadDocument方法完成的。在看这个方法之前,先来看下getEntityResolver方法、getValidationModeForResource方法和isNamespaceAware方法的简单介绍。

准备工作

getEntityResolver()方法
/**
 * Return the EntityResolver to use, building a default resolver
 * if none specified.
 */
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;
}

可以看到,这个方法用来返回一个EntityResolver类型的对象。EntityResolver又是做什么的呢?

官方文档称,如果SAX应用程序需要实现自定义处理外部实体,则必须实现此接口并使用setEntityResolver方法向SAX驱动注册一个实例。

对于解析一个XML,SAX应用程序首先读取该XML文档上的声明,根据声明去寻找相应的DTD定义,以便对文档进行一个验证。默认的寻找规则,即通过网络(实现上就是声明的DTD的URL地址)来下载相应的dtd声明,并进行认证。下载的过程是一个漫长的过程,而且当网络中断或不可用时,这里就会报错的,就是因为相应的DTD声明没有被找到的原因。
EntityResolver的作用就是项目本身就可以提供一个如何寻找dtd声明的方法,即由程序来实现寻找dtd声明的过程,比如我们将dtd文件放到项目中某处,在实现时直接将此文档读取并返回给sax,即可。这样就避免了通过网络来寻找相应的声明。

getValidationModeForResource方法
/**
 * Gets the validation mode for the specified {@link Resource}. If no explicit
 * validation mode has been configured then the validation mode is
 * {@link #detectValidationMode detected}.
 * <p>Override this method if you would like full control over the validation
 * mode, even when something other than {@link #VALIDATION_AUTO} was set.
 */
protected int getValidationModeForResource(Resource resource) {
    int validationModeToUse = getValidationMode();
    if (validationModeToUse != VALIDATION_AUTO) {
        return validationModeToUse;
    }
    int detectedMode = detectValidationMode(resource);
    if (detectedMode != VALIDATION_AUTO) {
        return detectedMode;
    }
    // Hmm, we didn't get a clear indication... Let's assume XSD,
    // since apparently no DTD declaration has been found up until
    // detection stopped (before finding the document's root tag).
    return VALIDATION_XSD;
}

这个方法用来读取XML文件的验证模式。XML的验证模式保证了XML文件的正确性。常用的验证模式有DTD何XSD两种。关于XML的验证模式以后会详细讲解。

isNamespaceAware方法
/**
 * Return whether or not the XML parser should be XML namespace aware.
 */
public boolean isNamespaceAware() {
    return this.namespaceAware;
}

这个方法用来返回XML解析器是否应该是XML命名空间知道的,this.namespaceAware默认值为false。如果为true,则XML命名空间必须提供。

loadDocument方法

org.springframework.beans.factory.xml.DefaultDocumentLoader.loadDocument(InputSource, EntityResolver, ErrorHandler, int, boolean)

/**
 * Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured
 * XML parser.
 */
@Override
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);
}

方法分为三步:

  1. 创建DocumentBuilderFactory对象
  2. 创建DocumentBuilder对象
  3. 通过DocumentBuilder对象解析inputSource并返回。

DocumentBuilder是个抽象类,public abstract class DocumentBuilder。parse方法的具体实现为

org.apache.xerces.jaxp.DocumentBuilderImpl.parse(InputSource)

public Document parse(InputSource is) throws SAXException, IOException {
    if (is == null) {
        throw new IllegalArgumentException(
            DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, 
            "jaxp-null-input-source", null));
    }
    if (fSchemaValidator != null) {
        if (fSchemaValidationManager != null) {
            fSchemaValidationManager.reset();
            fUnparsedEntityHandler.reset();
        }
        resetSchemaValidator();
    }
    domParser.parse(is);
    Document doc = domParser.getDocument();
    domParser.dropDocumentReferences();
    return doc;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值