数据准备阶段
准备的目的是封装
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:null
如果解析的验证模式是
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//EN
而对于不同的验证模式,
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文件就正式进入了解析步骤