spring源码:获取xml的验证模式

一、验证方式介绍

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>

二、相关类

  • org.springframework.beans.factory.xml.XmlBeanDefinitionReader:加载xml并注册Bean
  • org.springframework.util.xml.XmlValidationModeDetector:获取xml的验证方式

三、源码分析

XmlBeanFactory在初始化的时候,主要使用XmlBeanDefinitionReader来加载Bean的,我们的分析入口就从XmlBeanDefinitionReader类加载Bean的方法开始。

  1. 使用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);
		}
	}
  1. 使用XmlValidationModeDetector检测xml验证模式
	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模式。

五、借鉴

  1. org.springframework.util.xml.XmlValidationModeDetector
    使用可读性强的单词,来代表某种意义的标识。
public static final int VALIDATION_DTD = 2;
  1. org.springframework.util.xml.XmlValidationModeDetector
    使用private static final标识本类使用的常量。
private static final String DOCTYPE = "DOCTYPE";

你可能感兴趣的:(spring源码)