首先,在我的这篇博客中已经说到容器是怎么初步实现的,并且要使用XmlBeanDefinitionReader对象对Xml文件进行解析,那么Xml文件是如何进行解析的,将在这片博客中进行一些陈述.
数据准备阶段
准备的目的是封装resource参数,目的是为了考虑到Resource可能存在编码要求的情况,其次,通过SAX读取XML文件的方式来准备InputSource对象,最后将参数传递到最核心的实现部分doLoadBeanDefinitions(inputSource,encodedResource.getResource())
封装Resource
调用XmlBeanDefinitionReader的loadBeanDefinitions(Resource resource)方法时,首先将resource对象进行再次封装成EncodedResource,查看源码可以发现里面增加了字符集和编码的封装,从命名上来看也可以体现出来,将资源封装完成后,就调用重载的同名函数loadBeanDefinitions(EncodedResource resource)进行正式的解析.
数据准备操作
在重载方法里面首先通过Set<EncodedResource> currentResources属性来记录已经加载的资源,其次,从EncodedResource对象中获取封装好的Resource对象,并获取其inputStream,将获取到的输入流与SAX解析的InputSource绑定,接下来就进入到了核心的实现部分:doLoadBeanDefinitions(inputSource,encodedResource.getResource())
核心实现
核心部分有两个关键步骤:
- 调用
doLoadDocument(inputSource.resource)方法获取Document - 根据返回的
Document信息注册Bean信息
这两个步骤支持着整个Spring容器部分的实现基础
获取Document
进入方法体后,将Document的创建交给DefaultDocumentLoader documentLoader属性的loadDocument()方法,该方法声明如下:
Document loadDocument(
InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware)
throws Exception;
调用情况:
documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware())
InputSource:SAX解析需要使用到的对象EntityResolver:它的作用是项目本身就可以提供一个如何寻找DTD声明的方法,由程序来实现寻找DTD声明的过程,将DTD文件放到项目中某处,在实现时直接将此文档读取并返回给SAX即可,避免了必须通过网络来寻找相应的声明.
在这个接口中定义了一个方法
InputSource resolveEntity (String publicId,String systemId)
throws SAXException, IOException;
如果解析的验证模式是XSD:
<?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>
那么.此时得到的两个参数值分别是:
publicId:nullsystemId:http://www.springframework.org/schema/beans/spring-beans.xsd
如果解析的验证模式是DTD:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//Spring//DTD BEAN 2.0//EN" "http://www.Springframework.org/dtd/Spring-beans-2.0.dtd"
那么,此时得到的两个参数值分别是:
publicId:-//Spring//DTD BEAN 2.0//ENsystemId:http://www.Springframework.org/dtd/Spring-beans-2.0.dtd
而对于不同的验证模式,Spring使用了不同的解析器,当使用DTD验证时,Spring会截取后面的*.dtd,并直接到当前目录去寻找,当使用XSD验证时,Spring会到META-INF/Spring.schemas文件中去匹配相应的systemId并加载对应的XSD文件
validationMode:验证模式
首先,为了保证XML文件的正确性,有常见两种验证模式:DTD、XSD
两种验证模式的区别
我对这两种的区别目前还不是很详细,只能简略的给出定义,但我看到的最直观的区别是,DTD验证需要单独写出一个标签<!DOCTYPE ...>,而XSD验证会将信息写入<beans xmlns="...">结点
DTD
DTD(Document Type Definition)即文档类型定义,是一种保证XML文档格式正确的有效方法,可以通过比较XML文档和DTD文件来看文档是否符合规范.
XSD
XML Schema语言就是XSD(XML Schema Definition),描述了XML文档的结构,可以用一个指定的XML Schema来验证XML文档,以检查文档是否符合要求.
验证模式的读取
验证模式的读取非常简单,在getValidationModeForResource(resource)方法中先获取当前设定的验证模式是不是自动选择,源码中是这么解释的since we cannot find a clear indication,当找不到一个确切的验证模式时,采用这种方式,然后判断当前resource对象中采用的是什么验证模式,通过检索字符串的方式,当存在DOCTYPE的时候,就采用DTD验证模式,否则采用XSD验证模式
namespaceAware:一个布尔值,默认为false,在前面可以看到,在使用XSD验证的时候会有xmlns="",其实就是XML namespace的缩写,可以有多个命名空间,如果使用的是XSD解析,将会把这个值改为true
解析并注册BeanDefinitions
在上一步得到Docment对象之后,调用registerBeanDefinitions(Document doc,Resource resource)
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//创建对象
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//记录当前已经加载的数量
int countBefore = getRegistry().getBeanDefinitionCount();
//加载并注册
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//返回本次加载的个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}
而在调用documentReader对象方法中,才开始进行正式的解析工作
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
解析的工作全权交给doRegisterBeanDefinition(root)方法实现,这样XML文件就正式进入了解析步骤,至于怎么解析的,博主将慢慢学习并写入后续博客.
来源:https://www.cnblogs.com/yuanmiemie/p/6843586.html