spring验证xml文件文件的过程

  获取xsd文件信息

  spring中使用xsd文件定义bean标签元素,spring容器加载xml配置文件时,会先获取xsd文件去验证xml文件的正确性。

  获取xsd文件可直接请求xsd文件的URL即可,但是在生产环境往往不可访问公网,或者由于网络的抖动导致请求的不可达,导致请求xsd文件出错,进而导致整个spring容器初始化失败;为避免这个问题,spring将对应的xsd文件放到本地jar中,容器初始化时优先从本地加载。

  xsd文件定义一般定义在/META-INF/spring.schemas文件中,例如spring-data-commons-2.1.10.RELEASE.jar中的spring.schemas提供了了根据xsd文件的url映射到对应的xsd文件的信息。

记一次问题排查发现的spring-ldap的bug_第1张图片

  这个spring在解析xml文件拿到对应的xsd的url后,就可以根据上面的信息找到xsd文件了,不用通过网络请求,然后就行可解析xsd文件元整xml文件的的正确性。

  xsd文件的加载和寻找过程

  查看springframwork源码PluggableSchemaResolver类定义了加载/META-INF/spring.schemas文件的过程,加载全部jar中的/META-INF/spring.schemas并将xsd的url和在jar中的路径保存在map中,在解析时直接根据xsd的url去map中寻找xsd文件的位置来获取。

  PluggableSchemaResolver.java

记一次问题排查发现的spring-ldap的bug_第2张图片

记一次问题排查发现的spring-ldap的bug_第3张图片

记一次问题排查发现的spring-ldap的bug_第4张图片

  问题描述

  软件版本信息

  spring framework:4.3.13.RELEASE

  spring ldap:2.3.2.RELEASE

  功能背景

  ldap方式查询公司人员信息

  主要实现代码

 记一次问题排查发现的spring-ldap的bug_第5张图片


  UserDao.java

记一次问题排查发现的spring-ldap的bug_第6张图片

  UserDaoImpl.java

记一次问题排查发现的spring-ldap的bug_第7张图片

记一次问题排查发现的spring-ldap的bug_第8张图片

  UserDaoTest.java

记一次问题排查发现的spring-ldap的bug_第9张图片

  问题描述

  上面的程序在本地机器上运行正常,发布到开发测试机上,抛出异常。

  异常堆栈

  org.xml.sax.SAXParseException; systemId: http://www.springframework.org/schema/ldap/spring-ldap.xsd; lineNumber: 9; columnNumber: 112; schema_reference.4: 无法读取方案文档 'http://www.springframework.org/schema/data/repository/spring-repository.xsd', 原因为 1) 无法找到文档; 2) 无法读取文档; 3) 文档的根元素不是。

记一次问题排查发现的spring-ldap的bug_第10张图片

记一次问题排查发现的spring-ldap的bug_第11张图片

记一次问题排查发现的spring-ldap的bug_第12张图片

  问题排查和定位

  从异常信息看,是获取和解析spring-repository.xsd文件出错,但是为什么会需要spring-repository.xsd呢?

  于是想到在spring容器初始化的过程中,需要解析xsd文件以验证xml文件格式的正确与否,获取和解析spring-repository.xsd出错应该是在这个过程中出现的。

  项目中并没有使用到spring-repository.xsd中定义的任何标签,为什么会加载它呢?

  初步怀疑是存在jar包依赖,但是检查后并不存在包含spring-repository.xsd文件的spring-data-commons的jar包;但是打开spring-ldap.xsd文件发现有如下代码:

记一次问题排查发现的spring-ldap的bug_第13张图片

  到此恍然大悟,原来是spring-ldap.xsd中又引用到spring-repository.xsd中的标签定义,所以解析时才回去找spring-repository.xsd文件,但本地没有spring-data-commons的jar包,所以才会尝试请求http://www.springframework.org/schema/data/repository/spring-repository.xsd 获取文件内容,但是开发环境的机器不能链接公网,而本地机器可以,所以才出现了上面的问题。

  于是,想着只要在在pom.xml中添加spring-data-commons的jar包依赖,问题就应该解决了吧。然而,并没有,依旧是本地机器可以,开发测试环境的机器依旧报上面的异常。

  查看spring-data-commons的jar包中的/META-INF/spring.schemas定义,如下:记一次问题排查发现的spring-ldap的bug_第14张图片

  发现,spring.schemas中的xsd文件url使用的是https,而在spring-ldap.xsd文件中引用的spring-repository.xsd文件的schemaLocation是http的,二者不一致,导致了去根据http的xsd文件url到map中找xsd文件路径时找不到。本地找不到之后,spring便尝试网络请求获取,而恰好网络请求不可达。

  经过debug后,进一步确认了我的问题定位:

  

记一次问题排查发现的spring-ldap的bug_第15张图片


  到此,定位到该问题为spring-ldap项目的bug,spring-ldap.xsd文件中引用的spring-repository.xsd文件的schemaLocation应该是https的。目前该问题已经在GitHub的spring-ldap上提出了issue等待解决。

  问题解决方法

  不使用spring自定义的ldap:标签元素,改为最基础的bean标签,这样就不会加载和解析spring-ldap.xsd文件了,最终的xml文件内用如下:

记一次问题排查发现的spring-ldap的bug_第16张图片