2.6 获取XML的验证方式
加载Bean的过程主要有三步,1、获取对XML的验证模式 2、加载XML文件,并且得到相应的doucument 3、根据返回的doucument来注册Bean的信息。接下来具体探讨一下Spring源码中怎么去完成的这三步。
2.6.1 DTD与XSD的区别
DTD:类型定义(Documnet Type Definition)
DTD 是一套关于标记符的语法规则。它是XML1.0版规格得一部分,是XML文件的验证机制,属于XML文件组成的一部分。
DTD 是一种保证XML文档格式正确的有效方法,可以通过比较XML文档和DTD文件来看文档是否符合规范,元素和标签使用是否正确。
一个DTD文档包含:元素的定义规则,元素间关系的定义规则,元素可使用的属性,可使用的实体或符号规则。
但是DTD 是使用非 XML 语法编写的
DTD 不可扩展,不支持命名空间,只提供非常有限的数据类型
XSD:XML结构定义 ( XML Schemas Definition )
XML Schema语言也就是XSD。XML Schema描述了XML文档的结构。
可以用一个指定的XML Schema来验证某个XML文档,以检查该XML文档是否符合其要求。文档设计者可以通过XML Schema指定一个XML文档所允许的结构和内容,并可据此检查一个XML文档是否是有效的。XML Schema本身是一个XML文档,它符合XML语法结构。可以用通用的XML解析器解析它。
一个XML Schema会定义:文档中出现的元素、文档中出现的属性、子元素、子元素的数量、子元素的顺序、元素是否为空、元素和属性的数据类型、元素或属性的默认和固定值。
XSD是DTD替代者的原因,一是据将来的条件可扩展,二是比DTD丰富和有用,三是用XML书写,四是支持数据类型,五是支持命名空间。
XML Schema的优点:
1) XML Schema基于XML,没有专门的语法
2) XML可以象其他XML文件一样解析和处理
3) XML Schema支持一系列的数据类型(int、float、Boolean、date等)
4) XML Schema提供可扩充的数据模型。
5) XML Schema支持综合命名空间
6) XML Schema支持属性组。
2.6.2 验证模式的读取
之前分析过Spring通过getValidationModeForResource(resource)这个方法进入到获取XML验证模式,下面看一下这个方法的源码
protected int getValidationModeForResource(Resource resource) {
int validationModeToUse = getValidationMode();
//自定义的验证方式
if (validationModeToUse != VALIDATION_AUTO) {
return validationModeToUse;
}
// 如果未指定验证方式,那么将会自动检测
int detectedMode = detectValidationMode(resource);
if (detectedMode != VALIDATION_AUTO) {
return detectedMode;
}
return VALIDATION_XSD;
}
方法的实现其实是很简单的,无非是如果设定了验证模式则使用验证模式(可以通过调用XmlBeanDefinitionReader中的setValidationModeName(String validationModeName)来进行自定义验证),否则执行detectValidationMode(resource)使用自动检测的方式。在detectValidationMode方法中,又将自动检测的验证模式委托给了ValidationModeDetector类,调用了detectValidationMode的方法。
protected int detectValidationMode(Resource resource) {1
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);
}
//委托了validationModeDetector
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);
}
}
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;
}
if (hasDoctype(content)) {
isDtdValidated = true;
break;
}
//当读到<这个得时候,则进行开始验证了,因为dtd,xsd都是以<开始的
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();
}
}
其实自动检测的业务逻辑很简单,就是判断是否包含DOCTYPE,如果包含就是DTD验证方式,否则就是XSD