协慌网

登录 贡献 社区

在 Java / Maven 中处理 “Xerces 地狱”?

在我的办公室里,仅提及 Xerces 一词就足以引起开发人员的致命愤怒。粗略浏览 SO 上的其他 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 以前以单个 jarxerces.jar )的形式发布,但被拆分为两个 jar,一个包含 API( xml-apis.jar ),另一个包含这些 API 的实现( xercesImpl.jar )。许多较旧的 Maven POM 仍然声明对xerces.jar的依赖。在过去的某个时候,Xerces 也以xmlParserAPIs.jar发布,某些较早的 POM 都依赖于 xmlParserAPIs.jar。

  • 将 jar 部署到 Maven 存储库的人分配给 xml-apis 和 xercesImpl jar 的版本通常是不同的。例如,即使 xml-apis 都来自 Xerces 2.8.0,也可以给它 xml 版本 1.3.03,给 xercesImpl 提供 2.8.0 版本。这是因为人们经常用实现的规范版本标记 xml-apis jar。还有就是这是一个非常不错的,但不完全击穿这里

  • 使事情复杂化的是,Xerces 是 JRE 中包含的 XML 处理 Java API(JAXP)的参考实现中使用的 XML 解析器。实现类在com.sun.*名称空间下重新打包,这使得直接访问它们很危险,因为它们在某些 JRE 中可能不可用。但是,并非所有 Xerces 功能都是通过java.* javax.* API 公开的;因此,请参见参考资料。例如,没有 API 公开 Xerces 序列化。

  • 令人困惑的是,几乎所有的 servlet 容器(JBoss,Jetty,Glassfish,Tomcat 等)都在其一个或多个/lib文件夹中随 Xerces 一起提供。

问题

解决冲突

由于上述某些(或全部)原因,许多组织在其 POM 中发布和使用 Xerces 的自定义版本。如果您的应用程序很小,并且仅使用 Maven Central,这并不是真正的问题,但是对于 Artifactory 或 Nexus 代理多个存储库(JBoss,Hibernate 等)的企业软件,这很快就成为问题:

由Artifactory代理的xml-apis

例如,组织 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 不知道它们是同一工件,因为它们具有不同的groupId 。因此,它无法执行冲突解决,并且两个jar都将作为已解决的依赖项包括在内:

使用多个xml-api解决已解决的依赖关系

类加载器地狱

如上所述,JRE 随 Xerces 一起提供在 JAXP RI 中。将所有 Xerces Maven 依赖项标记为<exclusion><provided>可能会很好,但是您所依赖的第三方代码可能与您正在使用的 JDK 的 JAXP 中提供的版本兼容或不兼容。另外,您还可以将 Xerces jars 放在 servlet 容器中以应对。这给您提供了许多选择:是否删除 servlet 版本,并希望您的容器在 JAXP 版本上运行?离开 servlet 版本更好,希望您的应用程序框架在 servlet 版本上运行是否更好?如果上面概述的一个或两个未解决的冲突成功地渗入了您的产品(在大型组织中很容易发生),您很快就会陷入类加载器地狱中,想知道类加载器在运行时选择的是哪个版本的 Xerces,将在 Windows 和 Linux 中选择相同的 jar(可能不是)。

解决方案?

我们已经尝试将所有 Xerces Maven 依赖项标记为<provided><exclusion> ,但是由于工件具有如此多的别名( xml-apisxercesxercesImplxmlParserAPIs等)。此外,我们的第三方 libs / frameworks 可能无法在 JAXP 版本或 servlet 容器提供的版本上运行。

我们如何最好地使用 Maven 解决这个问题?我们是否必须对依赖项进行这种细粒度的控制,然后依靠分层的类加载?是否有某种方法可以全局排除所有 Xerces 依赖关系,并强制我们所有的框架 / 库使用 JAXP 版本?


更新:Joshua Spiewak 已将 Xerces 构建脚本的修补程序版本上载到XERCESJ-1454 ,从而可以上载到 Maven Central。投票 / 观看 / 贡献这个问题,让我们一劳永逸地解决这个问题。

答案

自 2013 年 2 月 20 日以来,Maven Central 中有 Xerces 的 2.11.0 JAR (和源 JAR!)!请参阅Maven Central 中的 Xerces 。我想知道为什么他们没有解决https://issues.apache.org/jira/browse/XERCESJ-1454 ...

我用过:

<dependency>
    <groupId>xerces</groupId>
    <artifactId>xercesImpl</artifactId>
    <version>2.11.0</version>
</dependency>

并且所有依赖项都已很好地解决 - 甚至是正确的xml-apis-1.4.01

而且,最重要的一点(以及过去不明显的地方) Xerces-J-bin.2.11.0.zip Central 中的 JAR 与官方 Xerces-J-bin.2.11.0.zip 发行版中的 JAR 是相同的。

但是,我找不到xml-schema-1.1-beta版本 - 由于存在更多依赖关系,因此它无法成为 Maven classifier

坦白说,我们所遇到的几乎所有东西都可以在 JAXP 版本下正常运行,因此我们总是排除xml-apisxercesImpl

您可以将 Maven 强制实施器插件与禁止的依赖关系规则一起使用。这将允许您禁止所有不需要的别名,而只允许您想要的别名。违反这些规则将使您的项目的 Maven 构建失败。此外,如果此规则适用于企业中的所有项目,则可以将插件配置放入公司的父 pom 中。

看: