- 前言
- 第一部分 核心实现
- 第 1 章 Spring 整体架构和环境搭建
- 第 2 章 容器的基本实现
- 第 3 章 默认标签的解析
- 第 4 章 自定义标签的解析
- 第 5 章 bean 的加载
- 第 6 章 容器的功能扩展
- 第 7 章 AOP
- 第二部分 企业应用
- 第 8 章 数据库连接 JDBC
- 第 9 章 整合 MyBatis
- 第 10 章 事务
- 第 11 章 SpringMVC
- 第 12 章 远程服务
- 第 13 章 Spring 消息
2.5.2 加载 Bean
之前提到的在 XmlBeanFactory 构造函数中调用了 XmlBeanDefinitionReader 类型的 reader 属性提供的方法 this.reader.loadBeanDefinitions(resource),而这句代码则是整个资源加载的切入点,我们先来看看这个方法的时序图,如图 2-9 所示。
图 2-9 loadBeanDefinitions 函数执行时序图
看到图 2-9 我们才知道什么叫山路十八弯,绕了这么半天还没有真正地切入正题,比如加载 XML 文档和解析注册 Bean,一直还在做准备工作。我们根据上面的时序图来分析一下这里究竟在准备什么?从上面的时序图中我们尝试梳理整个的处理过程如下。
( 1 )封装资源文件。当进入 XmlBeanDefinitionReader 后首先对参数 Resource 使用 EncodedResource 类进行封装。
(2)获取输入流。从 Resource 中获取对应的 InputStream 并构造 InputSource。
(3)通过构造的 InputSource 实例和 Resource 实例继续调用函数 doLoadBeanDefinitions。
我们来看一下 loadBeanDefinitions 函数具体的实现过程:
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
那么 EncodedResource 的作用是什么呢?通过名称,我们可以大致推断这个类主要是用于对资源文件的编码进行处理的。其中的主要逻辑体现在 getReader() 方法中,当设置了编码属性的时候 Spring 会使用相应的编码作为输入流的编码。
public Reader getReader() throws IOException {
if (this.encoding != null) {
return new InputStreamReader(this.resource.getInputStream(), this.encoding);
}
else {
return new InputStreamReader(this.resource.getInputStream());
}
}
上面代码构造了一个有编码(encoding)的 InputStreamReader。当构造好 encodedResource 对象后,再次转入了可复用方法 loadBeanDefinitions(new EncodedResource(resource))。
这个方法内部才是真正的数据准备阶段,也就是时序图所描述的逻辑:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource. getResource());
}
//通过属性来记录已经加载的资源
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your
import definitions!");
}
try {
//从 encodedResource 中获取已经封装的 Resource 对象并再次从 Resource 中获取其中的 inputStream
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//InputSource 这个类并不来自于 Spring,它的全路径是 org.xml.sax.InputSource
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//真正进入了逻辑核心部分
returndoLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
//关闭输入流
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
我们再次整理一下数据准备阶段的逻辑,首先对传入的 resource 参数做封装,目的是考虑到 Resource 可能存在编码要求的情况,其次,通过 SAX 读取 XML 文件的方式来准备 InputSource 对象,最后将准备的数据通过参数传入真正的核心处理部分 doLoadBeanDefinitions(inputSource, encodedResource.getResource())。
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode,
isNamespaceAware());
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource
+ " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}
在上面冗长的代码中假如不考虑异常类的代码,其实只做了三件事,这三件事的每一件都必不可少。
(1)获取对 XML 文件的验证模式。
(2)加载 XML 文件,并得到对应的 Document。
(3)根据返回的 Document 注册 Bean 信息。
这 3 个步骤支撑着整个 Spring 容器部分的实现基础,尤其是第 3 步对配置文件的解析,逻辑非常的复杂,那么我们先从获取 XML 文件的验证模式开始讲起。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论