History
- Xerces是Java生态圈使用最广泛的XML解析器,基本上所有的类库和框架都会在一定程度上使用它(即使没有直接引用,也有可能间接引用)
- Xerces在官网中发布的包是没有标注版本的,2.11.0的jar命名为xercesImpl.jar而不是xercesImpl-2.11.0.jar.
- Xerces不使用Maven,不会上传官方的release版本到Maven的中央仓库。
- Xerces以前是发布一个jar包,但是之后分成两个jar包发布,一个包含API的xml-apis.jar和另一个包含其实现的xercesImpl.jar。许多老点儿的Maven Pom仍然依赖xerces.jar。更早的时候Xerces发布一个xmlParserAPIs.jar,也有些很古老很古老的pom会依赖这个jar。
- 发布到Maven仓库的一些jar经常会依赖版本不同的xml-apis和xercesImpl。举例来说,依赖的xml-apis的版本可能是1.3.03而依赖的xercesImpl版本可能就是2.8.0,即使两个包都是来自
Xerces 2.8.0。因为人们使用xml-apis.jar只想使用它某个版本的特定的API。 - 更麻烦的是,JRE中JAXP(Java API for XML Processing)的Reference implementation(参考实现?)使用的XML解析器就是用的这个鸟Xerces。实现类被重新打包进了com.sun.,从而导致直接引用这些类很危险,因为他们在某些JRE中可能不会包含。然而,Xerces中并不是所有的方法都通过java.和javax.*的API暴露。举例来说,就没有实现Xerces序列化的API。
- 基本上所有的servlet容器(JBoss, Jetty, Glassfish, Tomcat, ====),都包含一份或多份Xerces在他们的/lib包下。
问题
Maven解决jar包冲突
正是由于以上的一个或几个问题,许多组织会在他们的pom中重新构建自己的Xerces。当你的工程比较小而且你在用maven中央仓库的时候,这没什么问题。但是当你用Artifactory或者Nexus代理你的多个仓库时(JBoss的,Hibernate的,==),可能就出现问题了。
举例来说,A组织可能以如下方式依赖xml-apis:
<groupId>org.apache.xerces</groupId>
<artifactId>xml-apis</artifactId>
<version>2.9.1</version>
而B组织可能以如下方式依赖同样的jar包:
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
<version>1.3.04</version>
尽管B的jar版本比A的jar版本低,但是maven并不知道他们俩是同一个artifact,因为两个jar的groupId不同,那么,最后maven解决版本的冲突而是把两个jar同时引入项目。如下图:
类加载器Hell
上面提到的,JRE会在JAXP RI中包含Xerces。当你把maven依赖的所有的Xerces都标记成<exclusion>或者<provided>虽然看起来没什么问题,但是你依赖的第三方代码可能和你使用的JDK中的JAXP中Xerces版本不兼容。除此之外,你还要应付servlet容器中的包含的Xerces。这给了你几个选择:
- 删除servlet中的Xerces,然后祈祷你的容器可以在JAXP提供的版本上运行。
- 保留servlet中的那个版本,然后祈祷你的应用框架可以在servlet提供的Xerces版本上正常运行。
- 如果最后你的产品有那么一个或者两个没解决的版本冲突(如果你的应用很大的话,这是很容易出现的情况),你很快就会发现你正处于类加载器地狱(ClassLoader Hell),疑惑类加载器到底他妈的挑了那个版本在运行?在window或者linux上会不会挑同一个版本(很有可能不会)。
解决方案?
我们尝试把所有的Xerces的maven依赖标记成 或者,但是由于这鸟货的别名太多了(xml-apis, xerces, xercesImpl, xmlParserAPIs,====),这有时候并无卵用。另外,我们依赖的第三方包或者框架很可能不会跑在JAXP的版本或者servlet容器提供的Xerces版本上。
到底怎么解决?
目前他们的解决方案如下:
可以尝试使用maven enforcer插件中的banned dependency。这个rule可以让你禁止所有你不喜欢的别名,而且只会加入你喜欢的。