• 通俗理解spring源码(三)—— 获取xml的验证模式


    通俗理解spring源码(三)—— 获取xml的验证模式

    上一篇讲到了xmlBeanDefinitionReader.doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法。

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
                throws BeanDefinitionStoreException {
    
            try {
                //从资源文件转换为document对象
                Document doc = doLoadDocument(inputSource, resource);
                //解析document,并注册beanDefiniton到工厂中
                int count = registerBeanDefinitions(doc, resource);
                if (logger.isDebugEnabled()) {
                    logger.debug("Loaded " + count + " bean definitions from " + resource);
                }
                return count;
            }
            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);
            }
        }

     在该方法中,首先就是将资源文件装换为document对象

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

       通过getValidationModeForResource(resource)获取xml文件的验证模式。

    xml文件有两种校验模式,DTD和XSD,这里简单介绍一下:

    1、DTD校验模式

    DTD(Document Type Definition)即文档类型定义,是一种xml约束模式语言,是xml文件的验证机制,属于xml文件的一部分。DTD是一种保证xml文档格式正确的有效方法,可以通过比较xml文档和DTD文件来看文档是否符合规范,元素和标签使用是或否正确。一个DTD文档包含:元素的定义规则,元素间关系的定义规则,元素可使用的属性,可使用的实体或符号规则。

    这个DTD文件,可以直接写在xml内部,如:

    <?xml version="1.0"?>
    <!DOCTYPE note [
      <!ELEMENT note (to,from,heading,body)>
      <!ELEMENT to      (#PCDATA)>
      <!ELEMENT from    (#PCDATA)>
      <!ELEMENT heading (#PCDATA)>
      <!ELEMENT body    (#PCDATA)>
    ]>
    <note>
      <to>George</to>
      <from>John</from>
      <heading>Reminder</heading>
      <body>Don't forget the meeting!</body>
    </note>

    也可以外部引用,比如将DTD内容写在与xml文件同目录的note.dtd中,如:

    <?xml version="1.0"?>
    <!DOCTYPE note SYSTEM "note.dtd">
    <note>
    <to>George</to>
    <from>John</from>
    <heading>Reminder</heading>
    <body>Don't forget the meeting!</body>
    </note> 

       还可以引用网络上的DTD文件,如在我们最熟悉的mybatis配置文件中:

    <!DOCTYPE configuration
      PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
      "http://mybatis.org/dtd/mybatis-3-config.dtd">

      引用外部DTD文件,一定会有<!DOCTYPE >声明!

      关于DTD文档的详细语法,可以参考https://www.w3school.com.cn/dtd/index.asp

    2、XSD验证模式

      XML Schema语言就是XSD(XML Schemas Definition)。XML Schema描述了xml文档的结构,可以用一个指定的XML Schema来验证某个XML文档,以检查该xml文档是否符合要求。文档设计者可以通过XML Schema指定xml文档所允许的结构和内容,并可据此检查xml文档是否是有效的。XML Schema本身是xml文档,它符合xml语法结构。可以用通用的xml‘解析器解析它。

      XSD比DTD更加强大,可针对未来的需求进行扩展,基于 XML 编写,支持数据类型,支持命名空间等。

      一个xml文件中可以引入多个命名空间,每个命名空间都要与一个前缀绑定,或者没有前缀,作为默认命名空间,并且每个命名空间都要指定其对应的xml Schema文件位置或URL位置,如在spring配置文件中:

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

      其中,

    xmlns="http://www.springframework.org/schema/beans
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd

      表示引入beans作为默认命名空间,相对应的xsd文件在http://www.springframework.org/schema/beans/spring-beans-4.3.xsd中,要使用该命名空间的标签,不用加前缀。

    xmlns:context="http://www.springframework.org/schema/context"
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd

       表示引入context命名空间,并与context前缀相绑定,相对应的xsd文件在http://www.springframework.org/schema/context/spring-context-4.3.xsd中,即要使用该命名空间的标签,需要加context前缀,比如说我们最熟悉的  <context:component-scan base-package=""></context:component-scan>。

      关于XSD文档的详细语法,可以参考https://www.w3school.com.cn/schema/index.asp

     3、验证模式的读取

      了解了DTD和XSD的区别后再去分析spring中对于验证模式的获取就容易多了。

      接着来看getValidationModeForResource(resource)。

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

       这里逻辑很简单,作者的注释也很有意思,就是说我们无法清楚的知道准确的验证模式,如果在找到文档的根标签之前还没有找到明显的DTD声明,则推测为XSD验证模式。

      继续看一下detectValidationMode(resource)方法:

        protected int detectValidationMode(Resource resource) {
            if (resource.isOpen()) {
                throw new BeanDefinitionStoreException(
                        "Passed-in Resource [" + resource + "] contains an open stream: " +
                        "cannot determine validation mode automatically. Either pass in a Resource " +
                        "that is able to create fresh streams, or explicitly specify the validationMode " +
                        "on your XmlBeanDefinitionReader instance.");
            }
    
            InputStream inputStream;
            try {
                inputStream = resource.getInputStream();
            }
            catch (IOException ex) {
                throw new BeanDefinitionStoreException(
                        "Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " +
                        "Did you attempt to load directly from a SAX InputSource without specifying the " +
                        "validationMode on your XmlBeanDefinitionReader instance?", ex);
            }
    
            try {
                return this.validationModeDetector.detectValidationMode(inputStream);
            }
            catch (IOException ex) {
                throw new BeanDefinitionStoreException("Unable to determine validation mode for [" +
                        resource + "]: an error occurred whilst reading from the InputStream.", ex);
            }
        }

       又是委派模式,由validationModeDetector进行处理,进入validationModeDetector.detectValidationMode(inputStream)中:

        public int detectValidationMode(InputStream inputStream) throws IOException {
            // Peek into the file to look for DOCTYPE.
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            try {
                boolean isDtdValidated = false;
                String content;
                //一行行读取文件内容
                while ((content = reader.readLine()) != null) {
                    //去掉文件的注释内容
                    content = consumeCommentTokens(content);
                    if (this.inComment || !StringUtils.hasText(content)) {
                        continue;
                    }
                    //判断该行是否包含DOCTYPE这个字符串
                    if (hasDoctype(content)) {
                        isDtdValidated = true;
                        break;
                    }
                    //判断该行是否包含开始标签符号,即"<"
                    if (hasOpeningTag(content)) {
                        // End of meaningful data...
                        break;
                    }
                }
                return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
            }
            catch (CharConversionException ex) {
                // Choked on some character encoding...
                // Leave the decision up to the caller.
                return VALIDATION_AUTO;
            }
            finally {
                reader.close();
            }
        }

        private boolean hasDoctype(String content) {
            return content.contains(DOCTYPE);
        }
        private boolean hasOpeningTag(String content) {
            if (this.inComment) {
                return false;
            }
            int openTagIndex = content.indexOf('<');
            return (openTagIndex > -1 && (content.length() > openTagIndex + 1) &&
                    Character.isLetter(content.charAt(openTagIndex + 1)));
        }

      一行行读取文件内容,去掉文件的注释内容,首先判断该行是否包含DOCTYPE这个字符串,如果有则判定为VALIDATION_DTD,如果没有,再判断该行是否包含开始标签符号,如果有,则判定VALIDATION_XSD,如果没有,则读取下一行。

      获取xml验证模式的逻辑并不复杂,主要是要知道DTD和XSD的区别。

      走的太远,不要忘记为什么出发!获取校验模式的目的是要对xml文件进行校验,然后解析成document。

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

       下一章将讲解documentLoader.loadDocument,获取Document。

    参考:https://www.w3school.com.cn/

       https://www.cnblogs.com/osttwz/p/6892999.html

       spring源码深度解析

  • 相关阅读:
    dp周训练 状态压缩
    计算几何 点对处理 #345 (Div. 2) C. Watchmen
    Codeforces Round #303 (Div. 2) D. Queue 水题贪心
    TTTTTTTTTTTTT 树的直径 Codeforces Beta Round #14 (Div. 2) D. Two Paths
    TTTTTTTTTTTTT CF Good Bye 2015 C- New Year and Domino(CF611C) 二维前缀
    巨蟒python全栈开发数据库前端8:jQuery框架2
    计时器练习
    onload事件,解决不能在head写代码
    js引入方式的弹框方法2
    pycharm中格式标准化代码
  • 原文地址:https://www.cnblogs.com/xiaohang123/p/12709192.html
Copyright © 2020-2023  润新知