Spring中XML的验证模式

Spring中XML的验证模式,第1张

XML文件的验证模式保证了XML文件的正确性,而比较常用的验证模式有两种:DTD、XSD.

DTD与XSD的区别

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

DTD的引入分为两种方式:
内部引用
内部引用应当通过下面的语法包装在一个 DOCTYPE 声明中:

<!DOCTYPE 根元素 [元素声明]>

带有DTD的XMl文档实例

<?xml version="1.0"?>
// note是根元素
<!DOCTYPE note [
   // 定义 note 元素有四个元素:"to、from、heading,、body"
  <!ELEMENT note (to,from,heading,body)>
  // to 元素为 "#PCDATA" 类型,以下类推
  <!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>

外部引用
外部引用应通过下面的语法被封装在一个 DOCTYPE 定义中:
1、创建myDtd.dtd

// SYSTEM 引用的是本地文件
// PUBLIC 引用的是文件的网络地址
<!DOCTYPE 根元素 SYSTEM/PUBLIC "文件名">

2、创建dtd文件

// 定义 note 元素有四个元素:"to、from、heading,、body"
<!ELEMENT note (to,from,heading,body)>
// to 元素为 "#PCDATA" 类型,以下类推
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>

3、引入文件

<?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> 

这里只是举一个小的例子,如果感兴趣可以查看W3cSchool手册

Mapper.xml文件
下面用一个mapper的配置文件来看下,DTD文件的PUBLIC的使用:


DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="xx.xxx.xxx.xxxMapper">

mapper>

具体mybatis-3-mapper.dtd部分如下:


<!ELEMENT mapper (cache-ref | cache | resultMap* | parameterMap* | sql* | insert* | update* | delete* | select* )+>
<!ATTLIST mapper
namespace CDATA #IMPLIED
>
<!ELEMENT cache-ref EMPTY>
<!ATTLIST cache-ref
namespace CDATA #REQUIRED
>
<!ELEMENT cache (property*)>
<!ATTLIST cache
type CDATA #IMPLIED
eviction CDATA #IMPLIED
flushInterval CDATA #IMPLIED
size CDATA #IMPLIED
readOnly CDATA #IMPLIED
blocking CDATA #IMPLIED
>

二、XSD(XML Schemas Definition)就是XML Schemas. XML Schemas描述了XML文档的结构.可以用个一个指定的XML Schemas来验证某个XML文档,以检查该XML文档是否符合其要求.文档设计者可以通过XML Schema指定XML文档所允许的结构和内容,并可据此检查XML文档是否有效.

用pom.xml文件来看一下XSD文件及用法
1、定义XSD文档

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://maven.apache.org/POM/4.0.0" elementFormDefault="qualified" targetNamespace="http://maven.apache.org/POM/4.0.0">
<xs:element name="project" type="Model">
xs:element name="project" type="Model">
xs:schema>
  • xmlns:xs="http://www.w3.org/2001/XMLSchema" :显示 schema 中用到的元素和数据类型来自命名空间 “http://www.w3.org/2001/XMLSchema”。同时它还规定了来自命名空间 “http://www.w3.org/2001/XMLSchema” 的元素和数据类型应该使用前缀 xs:表示.
  • xmlns="http://maven.apache.org/POM/4.0.0" :默认的命名空间,不同的xml文件的默认命名空间不同.
  • targetNamespace="http://maven.apache.org/POM/4.0.0:显示被此 schema 定义的元素 (project…) 来自命名空间: “http://maven.apache.org/POM/4.0.0”。
  • elementFormDefault="qualified:指出任何 XML 实例文档所使用的且在此 schema 中声明过的元素必须被命名空间限定.

2、使用XSD文档


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
project>
  • xmlns是是命名空间:xmlns=“http://maven.apache.org/POM/4.0.0”;和上面的默认命名空间对应
  • xsi:schemaLocation是用来指定命名空间所对应的XML Schema文档的存储位置.其中,他又分为两部分:一部分是名称空间的URI(http://maven.apache.org/POM/4.0.0);另一部分是该名称空间所标识的XML Schema文件位置或URL地址(https://maven.apache.org/xsd/maven-4.0.0.xsd).
  • xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance",本xml文件中要用到“http://www.w3.org/2001/XMLSchema-instance”这个命名空间的元素,比如用来引入无命名空间schema文件noNamespaceSchemaLocation=“XXX”;以及引入自带命名空间的schema文件的schemaLocation="XXX"这些元素。这些元素是包含在xsi命名空间中的,所有的xml文件只要引用这些元素 就要引入xsi这个命名空间。xsi这三个字母不是硬性规定,只是大家都这么用,方便阅读而已。
    当然有兴趣深入理解也是可以去查看[W3cSchool手册].(https://www.w3school.com.cn/schema/index.asp)
Spring验证模式的读取

上一小节提到Spring在读取配置文件的时候,把配置文件转成inputSource之后就是进行解析了.解析主要又分为两大步:1、将inputSource解析成document 2、然后进一步将document解析成BeanDefinition并注册.

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {

		try {
			// 解析成Document 
			Document doc = doLoadDocument(inputSource, resource);
			// 进一步解析,并注册BeanDefinition
			// count:是通过配置文件注册的BeanDefinition的个数
			int count = registerBeanDefinitions(doc, resource);
			return count;
		}
		catch (Exception ex) {
			throw ex;
		}
	}

1、Document的解析

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

doLoadDocument的方法里面其实又调了documentLoader.loadDocument()方法,我们可以发现这里:getValidationModeForResource(resource),这个方法的作用就是获取resource资源的验证模式的.

protected int getValidationModeForResource(Resource resource) {
		// 获取 validationMode的值,如果设定了,并且不是自动检测模式,就直接返回
		int validationModeToUse = getValidationMode();
		if (validationModeToUse != VALIDATION_AUTO) {
			return validationModeToUse;
		}
		// 自动检测模式,如果自动检测模式返回了具体的检验模式就直接返回
		// 否则就返回XSD模式
		int detectedMode = detectValidationMode(resource);
		if (detectedMode != VALIDATION_AUTO) {
			return detectedMode;
		}
		// 这里是说,因为没有得到明确的信息,来证明是哪种验证模式,所以先假设是XSD模式
		return VALIDATION_XSD;
	}

第一种情况是调用XmlBeanDefinitionReader的setValidationMode方法进行设置.
第二种是Spring根据配置文件是否包含DOCTYPE来判断,如果包含就是DTD,不包含就是XSD.

自动检测的代码

	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
			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 {
		// XmlValidationModeDetector的detectValidationMode做验证
			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);
		}
	}

	 */
	public int detectValidationMode(InputStream inputStream) throws IOException {
		// Peek into the file to look for DOCTYPE.
		// 上迷啊的英文就是源码给的注释,意思是找文件中是否包含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();
		}
	}

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/langs/920322.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-16
下一篇 2022-05-16

发表评论

登录后才能评论

评论列表(0条)

保存