在我的办公室里,仅仅提到Xerces一词就足以激起开发商的愤怒。粗略地看一下Xerces关于SO的其他问题,似乎表明几乎所有Maven用户都在某个时刻被这个问题“触动”了。不幸的是,了解这个问题需要一些关于薛西斯历史的知识。。。

历史

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(xmlapis.jar),另一个包含这些API的实现(xercesImpl.jar)。许多旧的Maven POM仍然声明对Xerces.jar的依赖。在过去的某个时候,Xerces也被发布为xmlParserAPIs.jar,一些旧的POM也依赖于此。将jar部署到Maven存储库的人员分配给xmlapi和xercesImpl jar的版本通常不同。例如,xml api的版本可能为1.3.03,xercesImpl的版本可能是2.8.0,尽管两者都来自Xerces 2.8.0。这是因为人们经常用它实现的规范版本来标记xmlapis jar。这里有一个非常好的,但不完整的分解。更复杂的是,Xerces是JRE中包含的Java API for XML Processing(JAXP)参考实现中使用的XML解析器。实现类在com.sun.*命名空间下重新打包,这使得直接访问它们很危险,因为它们在某些JRE中可能不可用。然而,并非所有Xerces功能都通过java.*和javax.*API公开;例如,没有公开Xerces序列化的API。更令人困惑的是,几乎所有的servlet容器(JBoss、Jetty、Glassfish、Tomcat等)都在一个或多个/lib文件夹中附带Xerces。

问题

冲突解决方案

出于上述原因组织在其聚甲醛。如果您有一个小型应用程序,并且只使用Maven Central,这并不是一个真正的问题,但对于Artifactry或Nexus代理多个存储库(JBoss、Hibernate等)的企业软件来说,这很快就会成为一个问题:

例如,组织A可以将xmlapi发布为:

<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不知道它们是同一个工件,因为它们有不同的组ID。因此,它无法执行冲突解决和两者jar将作为已解析的依赖项包含:

Classloader地狱

如上所述,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>,但这很难实施(尤其是在大型团队中),因为工件有太多别名(xmlapi、Xerces、xercesImpl、xmlParserAPI等)。此外,我们的第三方libs/framework可能无法在JAXP版本或servlet容器提供的版本上运行。

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


更新:Joshua Spiewak已将Xerces构建脚本的补丁版本上传到XERCESJ-1454,允许上传到Maven Central。投票/观看/贡献这个问题,让我们一劳永逸地解决这个问题。


当前回答

坦率地说,我们遇到的几乎所有东西在JAXP版本中都很好,所以我们总是排除xmlapi和xercesImpl。

其他回答

我想你需要回答一个问题:

是否存在一个xerces*.jar,您的应用程序中的所有内容都可以使用它?

如果不是的话,你基本上就完蛋了,必须使用OSGI这样的工具,它允许你同时加载不同版本的库。请注意,它基本上将jar版本问题替换为类加载器问题。。。

如果存在这样的版本,您可以让您的存储库为所有类型的依赖项返回该版本。这是一个丑陋的黑客,最终会在类路径中多次使用相同的xerces实现,但比使用多个不同版本的xerce要好。

您可以排除对xerces的每个依赖项,并将其添加到您想要使用的版本中。

我想知道你是否可以写一些版本解析策略作为maven的插件。这可能是最好的解决方案,但如果可行的话,需要一些研究和编码。

对于运行时环境中包含的版本,您必须确保在考虑服务器的lib文件夹之前,将其从应用程序类路径中删除,或者首先考虑应用程序jar进行类加载。

所以总结一下:这是一片混乱,不会改变。

自2013年2月20日以来,Maven Central有2.11.0个Xerces 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.401!

最重要的是(过去并不明显)——Maven Central中的JAR与Xerces-J-bin.2.11.0.zip官方发行版中的JAR相同。

然而,我找不到xml-schema-11-beta版本,因为附加的依赖性,它不能是Maven分类器ed版本。

您应该首先进行调试,以帮助确定您的XML地狱级别。我认为,第一步是添加

-Djavax.xml.parsers.SAXParserFactory=com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
-Djavax.xml.transform.TransformerFactory=com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
-Djavax.xml.parsers.DocumentBuilderFactory=com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl

到命令行。如果这样做有效,那么就开始排除库。如果没有,则添加

-Djaxp.debug=1

到命令行。

显然,xerces:xmlapis:1.4.01已经不在maven central中了,但是xerces:xercesImpl:2.11.0引用了这一点。

这对我有用:

<dependency>
  <groupId>xerces</groupId>
  <artifactId>xercesImpl</artifactId>
  <version>2.11.0</version>
  <exclusions>
    <exclusion>
      <groupId>xerces</groupId>
      <artifactId>xml-apis</artifactId>
    </exclusion>
  </exclusions>
</dependency>
<dependency>
  <groupId>xml-apis</groupId>
  <artifactId>xml-apis</artifactId>
  <version>1.4.01</version>
</dependency>

每个maven项目都应该停止依赖xerces,他们可能并没有真正依赖xerce。自1.4以来,XMLAPI和Impl一直是Java的一部分。不需要依赖于xerces或XMLAPI,就像说依赖于Java或Swing一样。这是含蓄的。

如果我是一个maven repo的老板,我会编写一个脚本递归地删除xerces依赖关系,并编写一个readme,说明这个repo需要Java1.4。

任何由于通过org.apache导入直接引用Xerces而导致实际中断的东西都需要一个代码修复,以将其提升到Java1.4级别(自2002年以来一直如此)或通过认可的libs(而不是maven)在JVM级别解决方案。