接上篇文章,链接: Spring 系列之 Spring 源码笔记:容器的基本实现-上【二】
4.2 加载 Bean
this.reader.loadBeanDefinitions(resource) 时序图:
从时序图梳理处理过程如下:
- 封装资源文件。当进入 XmlBeanDefinitionReader 后首先对参数 Resource 使用 EncodedResource 类进行封装。
- 获取输入流。从 Resource 中获取对应的 InputStream 并构造 InputSource。
- 通过构造的 InputSource 实例和 Resource 实例继续调用函数 doLoadBeanDefinitions。
下面是 loadBeanDefinitions 函数具体的实现过程:
上面的方法构造了一个有编码的 InputStreamReader。当构造好 encodedResource 对象后,再次转入了重载函数 loadBeanDefinitions,这个方法才是真正的数据准备阶段,也是时序图所描述的逻辑:
再次整理数据准备阶段的逻辑:
- 首先对传入的 resource 参数做封装,目的是考虑到 Resource 可能存在编码要求的情况;
- 通过 SAX 读取 XML 文件的方式来准备 InputSource 对象;
- 将准备的数据传入真正的核心处理部分 doLoadBeanDefinitions(inputSource, encodedResource.getResource())
核心处理部分代码如下图所示:
doLoadBeanDefinitions 函数其实只是做了两件事:
- 首先利用 DocumentLoader 获取 Document(具体细节后面进行分析);
- 根据返回的 Document 注册 Bean 信息,关于注册 Bean 信息就很复杂,后面会花大篇幅进行总结。
5. 获取 XML 的验证模式
XML 文件的验证模式主要用于保证 XML 文件的正确性,常用的验证模式主要有两种:DTD 和 XSD。
5.1 DTD 和 XSD 的区别
DTD(Document Type Definition)翻译一下就是文档类型定义,是一种 XML 约束模式语言,属于 XML 文件的一部分,可以通过 DTD 文件来查看 XML 文档的正确性。比如 Mybatis 的 xml 文档(如下图)。
XSD(XML Schemas Definition) 是 XML Schemas 语言,XML Schema 本身是 XML 文档,可以用一个指定的 XML Schema 来验证 XML 文档。比如 Spring 的配置文件:
使用 XSD 检验时,除了要声明名称空间外(xmlns=“http://www.springframework.org/schema/beans”),还必须指定该名称空间所对应的 XSD 文档的存储位置,通过 schemaLocation 属性来指定名称空间所对应的 XSD 文档的存储位置,它包含两个部分,一部分是名称空间的 URI,另一部分就是该名称空间所表示的 XSD 位置或 URL 地址(xsi:schemaLocation=“http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.2.xsd”)。
这里只是简单介绍下 XML 文件的验证模式,如果需要更深研究,可以进一步查阅相关资料。
5.2 验证模式的获取
通过之前的分析,得知 Spring 通过 getValidationModeForResource(resource)
方法来获取对应资源的验证模式:
通过注释继续看自动检测方法 detectValidationMode(resource) 的代码:
在该函数中将自动检测验证验证模式的工作委托给了专门处理类:XmlValidationModeDetector,通过调用 XmlValidationModeDetector 的 detectValidationMode 方法进行处理,代码如下:
Spring 用来检测验证模式的办法就是判断是否包含 DOCTYPE,如果包含就是DTD,否则就是XSD.
6. 获取 Document
获取验证模式后就可以加载 Document 了,和获取 XML 验证模式一样,对于文档读取 XmlBeanDefinitionReader 类没有自己实现,而是委托给了 DoucumentLoader 去执行,而 DocuemtLoader 是个接口,其实真正调用的是 DefaultDocumentLoader,解析代码如下:
通过 SAX 解析 XML 文档:
- 首先创建 DocumentBuilderFactory;
- 再通过 DocumentBuilderFactory 创建 DocumentBuilder;
- 最后通过 DocumentBuilder 解析 inputSource 来返回 Document 对象
其中第二步的参数中的 entityResolver 是通过函数 getEntityResolver() 获取的返回值传进来的,代码如下: