写在前面
本文在这篇文章的基础上继续分析,作为补充。
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文档对象的。