原文出自:http://cmsblogs.com作者:chenssy

在 XmlBeanDefinitionReader.doLoadDocument() 方法中做了两件事情,一是调用 getValidationModeForResource() 获取 XML 的验证模式,二是调用 DocumentLoader.loadDocument() 获取 Document 对象。上篇博客已经分析了获取 XML 验证模式(【死磕Spring】----- IOC 之 获取验证模型),这篇我们分析获取 Document 对象。 获取 Document 的策略由接口 DocumentLoader 定义,如下:

public interface DocumentLoader { Document loadDocument( InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception;}

DocumentLoader 中只有一个方法

loadDocument() ,该方法接收五个参数: * inputSource:加载 Document 的 Resource 源 * entityResolver:解析文件的解析器 * errorHandler:处理加载 Document 对象的过程的错误 * validationMode:验证模式 * namespaceAware:命名空间支持。如果要提供对 XML 名称空间的支持,则为true 该方法由 DocumentLoader 的默认实现类 DefaultDocumentLoader 实现,如下:

 public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception { DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware); if (logger.isDebugEnabled()) { logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]"); } DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler); return builder.parse(inputSource); }

首先调用

createDocumentBuilderFactory() 创建 DocumentBuilderFactory ,再通过该 factory 创建 DocumentBuilder,最后解析 InputSource 返回 Document 对象。

EntityResolver 通过

loadDocument() 获取 Document 对象时,有一个参数 entityResolver ,该参数是通过 getEntityResolver() 获取的。

getEntityResolver() 返回指定的解析器,如果没有指定,则构造一个未指定的默认解析器。

 protected EntityResolver getEntityResolver() { if (this.entityResolver == null) { ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader != null) { this.entityResolver = new ResourceEntityResolver(resourceLoader); } else { this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader()); } } return this.entityResolver; }

如果 ResourceLoader 不为 null,则根据指定的 ResourceLoader 创建一个 ResourceEntityResolver。如果 ResourceLoader 为null,则创建 一个 DelegatingEntityResolver,该 Resolver 委托给默认的 BeansDtdResolver 和 PluggableSchemaResolver 。

  • ResourceEntityResolver:继承自 EntityResolver ,通过 ResourceLoader 来解析实体的引用。
  • DelegatingEntityResolver:EntityResolver 的实现,分别代理了 dtd 的 BeansDtdResolver 和 xml schemas 的 PluggableSchemaResolver。
  • BeansDtdResolver : spring bean dtd 解析器。EntityResolver 的实现,用来从 classpath 或者 jar 文件加载 dtd。
  • PluggableSchemaResolver:使用一系列 Map 文件将 schema url 解析到本地 classpath 资源

getEntityResolver() 返回 EntityResolver ,那这个 EntityResolver 到底是什么呢?

If a SAX application needs to implement customized handling for external entities, it must implement this interface and register an instance with the SAX driver using the setEntityResolver method.就是说:如果 SAX 应用程序需要实现自定义处理外部实体,则必须实现此接口并使用 setEntityResolver() 向 SAX 驱动器注册一个实例。 如下:

 public class MyResolver implements EntityResolver { public InputSource resolveEntity (String publicId, String systemId){ if (systemId.equals("http://www.myhost.com/today")){ MyReader reader = new MyReader(); return new InputSource(reader); } else { // use the default behaviour return null; } } }

我们首先将

spring-student.xml 文件中的 XSD 声明的地址改掉,如下:

如果我们再次运行,则会报如下错误:

从上面的错误可以看到,是在进行文档验证时,无法根据声明找到 XSD 验证文件而导致无法进行 XML 文件验证。在(【死磕Spring】----- IOC 之 获取验证模型)中讲到,如果要解析一个 XML 文件,SAX 首先会读取该 XML 文档上的声明,然后根据声明去寻找相应的 DTD 定义,以便对文档进行验证。默认的加载规则是通过网络方式下载验证文件,而在实际生产环境中我们会遇到网络中断或者不可用状态,那么就应用就会因为无法下载验证文件而报错。

EntityResolver 的作用就是应用本身可以提供一个如何寻找验证文件的方法,即自定义实现。 接口声明如下:

public interface EntityResolver { public abstract InputSource resolveEntity (String publicId,String systemId) throws SAXException, IOException;}

接口方法接收两个参数 publicId 和 systemId,并返回 InputSource 对象。两个参数声明如下:

  • publicId:被引用的外部实体的公共标识符,如果没有提供,则返回null
  • systemId:被引用的外部实体的系统标识符 这两个参数的实际内容和具体的验证模式有关系。如下
  • XSD 验证模式
  • publicId:null
  • systemId:http://www.springframework.org/schema/beans/spring-beans.xsd
  • DTD 验证模式
  • publicId:-//SPRING//DTD BEAN 2.0//EN
  • systemId:http://www.springframework.org/dtd/spring-beans.dtd 如下:

我们知道在 Spring 中使用 DelegatingEntityResolver 为 EntityResolver 的实现类,resolveEntity() 实现如下:

 public InputSource resolveEntity(String publicId, @Nullable String systemId) throws SAXException, IOException { if (systemId != null) { if (systemId.endsWith(DTD_SUFFIX)) { return this.dtdResolver.resolveEntity(publicId, systemId); } else if (systemId.endsWith(XSD_SUFFIX)) { return this.schemaResolver.resolveEntity(publicId, systemId); } } return null; }

不同的验证模式使用不同的解析器解析,如果是 DTD 验证模式则使用 BeansDtdResolver 来进行解析,如果是 XSD 则使用 PluggableSchemaResolver 来进行解析。 BeansDtdResolver 的解析过程如下:

从上面的代码中我们可以看到加载 DTD 类型的

BeansDtdResolver.resolveEntity() 只是对 systemId 进行了简单的校验(从最后一个 / 开始,内容中是否包含 spring-beans),然后构造一个 InputSource 并设置 publicId、systemId,然后返回。 PluggableSchemaResolver 的解析过程如下:

首先调用 getSchemaMappings() 获取一个映射表(systemId 与其在本地的对照关系),然后根据传入的 systemId 获取该 systemId 在本地的路径 resourceLocation,最后根据 resourceLocation 构造 InputSource 对象。 映射表如下(部分):

preg_match 参数获取两个_「死磕 Spring」—– IOC 之 获取 Document 对象相关推荐

  1. 【死磕 Spring】----- IOC 之 Factory 实例化 bean

    原文:https://www.cmsblogs.com/category/1391374860344758272 『chenssy』 这篇我们关注创建 bean 过程中的第一个步骤:实例化 bean, ...

  2. 【死磕 Spring】----- IOC 之 加载 Bean

    原文:https://www.cmsblogs.com/category/1391374860344758272 『chenssy』 先看一段熟悉的代码: ClassPathResource reso ...

  3. 【死磕 Spring】—– IOC 之 Factory 实例化 bean

    这篇我们关注创建 bean 过程中的第一个步骤:实例化 bean,对应的方法为: createBeanInstance(),如下: protected BeanWrapper createBeanIn ...

  4. WPS如何并排放置两张图片_「WPS办公助手」想做出高级又好看的 PPT,这个炫酷的功能你可别错过...

    如果有人问我,有没有方法可以让 PPT 变得好看且更高级,我第一时间想到的就是它. 话不多说,直接上图: 这起源于keynote的"神奇移动",它可以使PPT里的元素实现非常炫酷的 ...

  5. 【死磕 Spring】----- IOC 之解析 bean 标签:解析自定义标签

    前面四篇文章都是分析 Bean 默认标签的解析过程,包括基本属性.六个子元素(meta.lookup-method.replaced-method.constructor-arg.property.q ...

  6. 【死磕 Spring】—– 死磕 Spring 精品合集

    Java面经 2019-03-05 09:00:40 原文:http://cmsblogs.com/?cat=206 [死磕 Spring]-– IOC 之深入理解 Spring IoC [死磕 Sp ...

  7. 「死磕Java并发编程」说说Java Atomic 原子类的实现原理

    <死磕 Java 并发编程>系列连载中,大家可以关注一波. 「死磕 Java 并发编程」阿里二面,面试官:说说 Java CAS 原理? 「死磕 Java 并发编程」面试官:说说什么是 J ...

  8. [死磕 Spring 21/43] --- IOC 之 Factory 实例化 bean

    引用原文: https://www.cmsblogs.com/article/1391375394573258752 [死磕 Spring 21/43] - IOC 之 Factory 实例化 bea ...

  9. 死磕Spring AOP系列2:剖析Bean处理器之BeanNameAutoProxyCreator

    通过前一篇<死磕Spring AOP系列1:编程式实现AOP>,学习了Spring对代理的底层支持,认识了ProxyFactory对象,及从类设计层面认识了PointCut&Adv ...

最新文章

  1. # 关闭 window10自带广告 microsoft star
  2. unable to get repr for class ‘torch.tensor‘
  3. Log4j每天、每小时、每分钟定时生成日志文件
  4. 大页(Huge Page)简单介绍
  5. 编号003099--ORACLE DBA-SH/WH
  6. 红米airdots掉了怎么查找_红米K30至尊版与realmeX7 Pro,两款性价比手机,谁才是第一位...
  7. Qt CMake变量参考
  8. 搭建kafaka_Kafka 环境部署搭建
  9. python默认参数只被解释一次_深入讲解Python函数中参数的使用及默认参数的陷阱...
  10. Mac OS修改VSCode Go的默认缩进格式
  11. Solr学习笔记001---solr在windows下的安装及配置
  12. html模块常用命名总结
  13. 存储设备在linux名称,Linux下的存储设备的管理
  14. 大学计算机应用与基础第二版答案,大学计算机应用基础(第2版)上机指导与习题集...
  15. 在LaTeX中使用Python highlighting in LaTeX让python代码高亮
  16. 思维的基本过程与解决问题
  17. android教你打造独一无二的图片加载框架
  18. 学Python好找工作吗?需要满足Python岗位哪些要求?
  19. 机器学习:SVR支持向量机回归
  20. 网页底部版权信息如何注明?

热门文章

  1. 开源与安全兼备 IBM LinuxONE轻松掌控关键业务
  2. 一个数据科学家对商学院的建议
  3. redhat 6.5 vnc 配置
  4. 读书笔记_Effective_C++_条款二十四: 若所有参数皆需类型转换,请为此采用non-member函数...
  5. linux下用cronolog分割apache日志
  6. 2018腾讯内部转岗面试题2——打印A-Z 26个字母的所有子集
  7. web前端开发初学者十问集锦(3)
  8. C++ 动态联编实现原理分析
  9. 去除QQ打/之后会出现的表情
  10. 裘宗燕:C/C++ 语言中的表达式求值