XML文件的验证模式保证了XML文件的正确性,比较常用的验证模式有两种:DTD和XSD。
DTD(Document Type Definition)文档类型定义,是一种XML约束模式语言,是XML文件的验证机制,属于XML文件组成的一部分。一个DTD文档包含:元素的定义规则,元素间关系的定义规则,元素可使用的属性,可使用的实体和符号规则。
DTD文档内容如下:
<!ELEMENT beans (
description?,
(import | alias | bean)*
)>
<!ATTLIST beans default-lazy-init (true | false) "false">
<!ATTLIST beans default-merge (true | false) "false">
<!ATTLIST beans default-autowire (no | byName | byType | constructor | autodetect) "no">
<!ATTLIST beans default-init-method CDATA #IMPLIED>
<!ATTLIST beans default-destroy-method CDATA #IMPLIED>
在xml文档中,声明格式如下
<beans>
... ...
beans>
XSD(XML Schemas Definition)就是XML Schema语言,描述了XML文档的结构。XML Schema指定XML文档所允许的结构和内容,并可据此检查XML文档是否是有效的,其本身也是XML文档符合XML语法结构。
XML Schema文档内容如下:
<xsd:schema xmlns="http://www.springframework.org/schema/beans"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.springframework.org/schema/beans">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:annotation>
<xsd:documentation>... ...xsd:documentation>
xsd:annotation>
<xsd:complexType name="identifiedType" abstract="true">
<xsd:annotation>
<xsd:documentation>... ...xsd:documentation>
xsd:annotation>
<xsd:attribute name="id" type="xsd:string">
<xsd:annotation>
<xsd:documentation> element.
]]>xsd:documentation>
xsd:annotation>
xsd:attribute>
xsd:complexType>
在xml文档中,声明格式如下:
<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>
XmlBeanFactory在初始化的时候,主要使用XmlBeanDefinitionReader来加载Bean的,我们的分析入口就从XmlBeanDefinitionReader类加载Bean的方法开始。
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 加载xml;点进去这个方法,继续这个方法分析
Document doc = doLoadDocument(inputSource, resource);
// 注册bean
return registerBeanDefinitions(doc, resource);
}
// 省略捕获的异常
...
}
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
// 第四个入参就是获取xml验证模式,也是本章分析的重点 点进去
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
// 这个入参是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;
}
// 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;
}
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 {
// 委托给XmlValidationModeDetector去检测验证模式,并传入输入流 继续分析这个
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;
// 一行行的读xml
while ((content = reader.readLine()) != null) {
content = consumeCommentTokens(content);
if (this.inComment || !StringUtils.hasText(content)) {
continue;
}
// 包含DOCTYPE就是DTD验证模式
if (hasDoctype(content)) {
isDtdValidated = true;
break;
}
// xml内容已开始,则未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
private boolean hasDoctype(String content) {
return content.contains(DOCTYPE);
}
// 是否已开始xml主要内容
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)));
}
XmlBeanDefinitionReader在doLoadDocument()方法调用getValidationModeForResource()获取验证模式的时候,其实是委托XmlValidationModeDetector的detectValidationMode()方法来获取的。判断xml的验证模式的规则是:某行是否包含“DOCTYPE”若包含则为DTD验证模式,若xml正式内容开始时还未检测到“DOCTYPE”则为XSD模式。
public static final int VALIDATION_DTD = 2;
private static final String DOCTYPE = "DOCTYPE";