spring IOC的验证模型分析

写在前面

本文在这篇文章的基础上继续分析,作为补充。

1:作用

验证的作用就是保证xml文件的正确性,只有保证了xml文件是完全正确才能继续后续的加载bean定义等操作。

2:xml验证机制

目前xml的验证机制有DTD(Document Type Definition),XSD(Xml Schema Definition),前者较早,XSD较晚,因为DTD的各种缺陷(如数据类型少,扩展性弱,非标准xml格式导致解析困难),已经被淘汰,XSD成为主流。

3:验证过程

为了方便调试再贴下测试代码:

@Test
public void testBeanDefinitionLoad() {
    // 定义资源
    ClassPathResource classPathResource = new ClassPathResource("testbeandefinition.xml");
    // 定义IOC容器
    DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
    // 定义bean定义读取器
    XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
    // 通过bean定义读取器从资源中读取bean定义
    int i = xmlBeanDefinitionReader.loadBeanDefinitions(classPathResource);
    System.out.println("bean定义的个数是:" + i);
}

xml:

<?xml version="1.0" encoding="UTF-8"?>
<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">

    <bean id="testBeanDefinitionBean"
          class="yudaosourcecode.spring.TestBeanDefinitionBean"></bean>

    <bean id="testBeanDefinitionBean1"
          class="yudaosourcecode.spring.TestBeanDefinitionBean"></bean>
    <!-- 这里引入自己的话会发生org.springframework.beans.factory.BeanDefinitionStoreException异常 -->
    <!--<import resource="testbeandefinition.xml"/>-->
</beans>

最终调用到代码:

org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadDocument
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
	return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
			getValidationModeForResource(resource), isNamespaceAware());
}

该方法内部会调用方法getValidationModeForResource获取验证模式,源码如下:

org.springframework.beans.factory.xml.XmlBeanDefinitionReader#getValidationModeForResource
protected int getValidationModeForResource(Resource resource) {
	// <2021-02-25 12:16> 获取设置的验证模式
	int validationModeToUse = getValidationMode();
	// 如果是设置的验证模式不是VALIDATION_AUTO,
	// private int validationMode = VALIDATION_AUTO;
	// public static final int VALIDATION_AUTO = XmlValidationModeDetector.VALIDATION_AUTO;
	// public static final int VALIDATION_AUTO = 1;
	// 则使用,否则继续
	if (validationModeToUse != VALIDATION_AUTO) {
		return validationModeToUse;
	}
	// <2021-02-25 12:21> 从资源中自动获取验证模式
	int detectedMode = detectValidationMode(resource);
	// 不是自动则使用自动获取的
	if (detectedMode != VALIDATION_AUTO) {
		return detectedMode;
	}
	// 手动也没有设置,自动也没有获取到,则默认XSD
	// public static final int VALIDATION_XSD = XmlValidationModeDetector.VALIDATION_XSD;
	// org.springframework.util.xml.XmlValidationModeDetector#VALIDATION_XSD
	// public static final int VALIDATION_XSD = 3;
	return VALIDATION_XSD;
}

<2021-02-25 12:16>处源码如下:

org.springframework.beans.factory.xml.XmlBeanDefinitionReader#getValidationMode
public int getValidationMode() {
	// 默认值为自动验证模式
	// private int validationMode = VALIDATION_AUTO;
	return this.validationMode;
}

<2021-02-25 12:21>从xml文件中获取验证方式,源码如下:

org.springframework.beans.factory.xml.XmlBeanDefinitionReader#detectValidationMode
protected int detectValidationMode(Resource resource) {
	// 不可读,抛出BeanDefinitionStoreException
	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) {
		...snip throw ex...
	}

	try {
		// <2021-02-25 14:40>这里为真正获取验证模式位置
		return this.validationModeDetector.detectValidationMode(inputStream);
	}
	catch (IOException ex) {
		...snip throw ex...
	}
}

<2021-02-25 14:40>这里为真正获取验证模式位置,我们单起一部分说明。

3.1:XmlValidationModeDetector

这是一个独立的对象,无子类,无父类当然排除java.lang.Object后。本部分讲解<2021-02-25 14:40>处代码,源码如下:

public int detectValidationMode(InputStream inputStream) throws IOException {
	// Peek into the file to look for DOCTYPE.
	BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
	try {
		// 默认是dtd
		boolean isDtdValidated = false;
		String content;
		// 循环每一行进行判断
		while ((content = reader.readLine()) != null) {
			// 处理注释
			content = consumeCommentTokens(content);
			// 如果被注释包裹或者是没有内容,则continue
			if (this.inComment || !StringUtils.hasText(content)) {
				continue;
			}
			// 如果是有DOCTYPE关键字,则认为是dtd,这里一般为false
			// 如下就是包含DOCTYPE的情况
			// <?xml version="1.0" encoding="UTF-8"?>
			// <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
			if (hasDoctype(content)) {
				isDtdValidated = true;
				break;
			}
			// 如果是以<开头,并且紧接着的是一个字母,则为true,因为不是我们需要的,而是正常的定义内容,如<bean...
			if (hasOpeningTag(content)) {
				// End of meaningful data...
				break;
			}
		}
		// 如果是isDtdValidated为true则返回dtd,否则xsd
		return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
	}
	catch (CharConversionException ex) {
		// 发生异常则返回自动验证模式
		return VALIDATION_AUTO;
	}
	finally {
		reader.close();
	}
}

接着通过这里,看下资源是如何加载为Document文档对象的。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值