本文正在参加「Java主题月 - Java Debug笔记活动」,详情查看活动链接
问题描述
在我的办公室里,仅仅是提到"Xerces"这个词就能引起开发者们的震怒了。我粗略浏览了一下在Stack Overflow上的其他关于Xerces的问题,发现几乎所有Maven用户在某个时候都被该问题“感动”了。不幸的是,要理解这个问题,就需要对Xerces的历史有所了解......
历史
- Xerces是Java生态系统中使用最广泛的XML解析器。几乎所有用Java编写的库或框架都以某种能力使用Xerces(如果不是直接使用,就是间接使用)。
- 到目前为止,官方二进制文件中包含的Xerces jar尚未进行版本控制。例如,Xerces 2.11.0实现的jar名为
xercesImpl.jar
,而不是xercesImpl-2.11.0.jar
。 - Xerces团队不使用Maven,这意味着他们不会将正式发行版上载到Maven Central。
- xerces曾经以一个单独的jar发布(
xerces.jar
),但分为两个jar包——一个包含API(xml-apis.jar
),一个包含这些API的实现(xercesImpl.jar
)。许多较旧的Maven POM仍然声明对xerces.jar
的依赖。在过去的某个时候,Xerces也以xmlParserAPIs.jar
的形式发布,还有某些较早的POM也会依赖。 - 被那些部署Maven存储库的jar分配给xml的api和xercesImpl jar版本通常是不同的。例如,xml的api可能考虑到版本1.3.03和xercesImpl可能给定版本2.8.0,即使是从xerces-c++2.8.0。这是因为人们常常标记xml的api和规范,它实现的版本不一致。有一个非常好的但不完整的说明在这里。
- 更复杂的是,Xerces是包括在JRE中用于XML处理的Java API的参考实现中的XML解析器(JAXP)。实现类在
com.sun.*
名字空间下被重新包装,这使得直接访问他们十分危险,因为他们可能没法在一些JRE中使用。然而,并不是所有Xerces的功能都是通过java.*
和javax.*
的API进行公开的;例如,没有API公开Xerces序列化。 - 更让人困惑的是,几乎所有的servlet容器(JBoss、Jetty、Glassfish、Tomcat等)在它们的
/lib
文件夹中都附带了Xerces。
问题
解决冲突
由于上述某些原因(或者可能是全部原因),许多组织在其POM中发布和使用Xerces的定制构建。如果你有一个小的应用程序,并且只使用Maven Central,这不是一个真正的问题,但它迅速成为企业软件的一个问题,Artifactory或联系代理多个存储库(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
是一个较低的版本,但由于它们有不同的groupId
,所以Maven不知道它们是相同的构件。因此,它不能执行冲突解决,并且jar
都将被包含为已解决的依赖项:
类加载器难题
如上所述,JRE在JAXP RI中是随着Xerces一起发布的。虽然最好将所有Xerces Maven依赖项标记为<exclusion>
或<provided>
,但您所依赖的第三方代码可能与您使用的JDK的JAXP中提供的版本兼容,同时也可能不兼容。此外,还有servlet容器中附带的Xerces jar需要处理。这给您留下了许多选择:是否删除servlet版本并希望容器在JAXP版本上运行?最好不要使用servlet版本,而希望您的应用程序框架运行在servlet版本上?如果上面列出的一个或两个未解决的冲突进入您的产品(这在大型组织中是很容易发生的),您很快就会发现自己处于类加载器地狱,想知道类加载器在运行时选择的Xerces版本以及是否将在Windows和Linux中选择相同的jar(可能不是)。
解决方案?
我们已经尝试将所有Xerces Maven依赖项标记为<provided>
或<exclusion>
,但这很难实施(特别是在大型团队中),因为构件有这么多别名(xml-api
、xerces
、xercesImpl
、xmlparserAPIs
等)。此外,我们的第三方库/框架可能不能在JAXP版本或servlet容器提供的版本上运行。
我们如何用Maven最好地解决这个问题?我们是否必须对依赖项进行这样的细粒度控制,然后依赖分层类加载?是否有某种方法可以全局排除所有Xerces依赖项,并强制所有框架/库使用JAXP版本?
更新:Joshua Spiewak已经上传了Xerces构建脚本的补丁版本到允许上传到Maven Central的XERCESJ-1454。
部分高赞回答
最高赞回答(115赞)
自2013年2月20日以来,Maven Central中有2.11.0 JAR (和JAR的源码)。详见Maven Central中的Xerces。我想知道为什么他们还没有解决:issues.apache.org/jira/browse… 我用过:
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.11.0</version>
</dependency>
而且所有的依赖都被很好地解决了——甚至是合适的xml-apis-1.4.01
!最重要的是(在过去并不明显)- Maven Central中的JAR与官方Xerces-J-bin.2.11.0.zip
发行版中的JAR是相同的。然而,我无法找到xml-schema-1.1-beta
版本——由于附加的依赖关系,它不能是Maven分类版。
第二高赞回答(64赞)
坦率地说,我们遇到的几乎所有东西在JAXP版本中都能正常工作,所以我们总是排除xml-apis和xercesImpl。
第三高赞回答(43赞)
你可以使用maven enforcer插件和被禁止的依赖规则。 这将允许您禁止所有您不想要的别名,只允许您想要的别名。 当违反这些规则时,项目的maven构建将失败。 此外,如果这个规则适用于一个企业中的所有项目,你可以把插件配置放到一个公司的父pom中。 详见:
第四高赞回答(36赞)
我知道这并不能准确地回答这个问题,但是对于那些从谷歌来的、碰巧使用Gradle进行依赖管理的人: 我设法摆脱所有的xerces/Java8问题与Gradle像这样:
configurations {
all*.exclude group: 'xml-apis'
all*.exclude group: 'xerces'
}
本文翻译自Stack Overflow,翻译部分参考来源自这里。关于翻译文章,聆风也相对生疏,难免有错漏,欢迎各位大佬在评论区批评指正,谢谢!