在Java/Maven中处理"Xerces hell"?

Jus*_*ick 703 java xerces classloader dependency-management maven

在我的办公室里,仅仅提到Xerces这个词就足以煽动开发者的凶悍愤怒.粗略地看一眼其他Xerces关于SO的问题似乎表明,几乎所有Maven用户都会在某个时候"触及"这个问题.不幸的是,理解这个问题需要对Xerces的历史有一点了解......

历史

  • Xerces是Java生态系统中使用最广泛的XML解析器.几乎每个用Java编写的库或框架都以某种身份使用Xerces(传递,如果不是直接的话).

  • 包含在官方二进制文件中的Xerces罐子直到今天还没有版本化.例如,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也依赖于它.

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

  • 更复杂的是,Xerces是包含在JRE中的Java API for XML Processing(JAXP)的参考实现中使用的XML解析器.实现类在com.sun.*命名空间下重新打包,这使得直接访问它们很危险,因为它们可能在某些JRE中不可用.但是,并非所有Xerces功能都通过API 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>
Run Code Online (Sandbox Code Playgroud)

同时,组织B可能会发布jar如下:

<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
<version>1.3.04</version>
Run Code Online (Sandbox Code Playgroud)

虽然B的jar版本低于A版jar,但Maven并不知道它们是同一个版本,因为它们有不同 groupId的版本.因此,它无法执行冲突解决,并且两个 jars都将作为已解析的依赖项包含在内:

使用多个xml-apis解决了依赖关系

Classloader Hell

如上所述,JRE在JAXP RI中附带Xerces.虽然将所有Xerces Maven依赖项标记为<exclusion>s或者很好<provided>,但是您所依赖的第三方代码可能会也可能不会与您正在使用的JDK的JAXP中提供的版本一起使用.此外,您还可以在servlet容器中附带Xerces jar以进行竞争.这给您留下了许多选择:您是否删除了servlet版本并希望您的容器在JAXP版本上运行?离开servlet版本是否更好,并希望您的应用程序框架在servlet版本上运行?如果上面列出的一个或两个未解决的冲突进入您的产品(很容易在大型组织中发生),您很快就会发现自己处于类加载器地狱,想知道类加载器在运行时选择哪个版本的Xerces以及是否将在Windows和Linux中选择相同的jar(可能不是).

解决方案?

我们试过标志认证的所有Xerces的Maven依赖作为<provided>或作为<exclusion>,但这是给难以执行(尤其是大型团队)的文物有这么多的别名(xml-apis,xerces,xercesImpl,xmlParserAPIs,等).此外,我们的第三方库/框架可能无法在JAXP版本或servlet容器提供的版本上运行.

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


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

Grz*_*bek 105

自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>
Run Code Online (Sandbox Code Playgroud)

并且所有依赖关系都已经解决了 - 甚至是正确的xml-apis-1.4.01!

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

然而,我无法找到xml-schema-1.1-beta版本 - classifier由于其他依赖性,它不能是Maven 版本.

  • 虽然'xml-apis:xml-apis:1.4.01`是_newer_而不是`xml-apis:xml-apis:2.0.2`是非常令人困惑的.见http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22xml-apis%22%20AND%20a%3A%22xml-apis%22 (9认同)

jta*_*orn 62

坦率地说,我们遇到的几乎所有东西都适用于JAXP版本,所以我们总是排除 xml-apisxercesImpl.

  • 你能为它添加一个pom.xml片段吗? (13认同)
  • 当我尝试这个时,我得到JavaMelody和Spring在运行时抛出`java.lang.NoClassDefFoundError:org/w3c/dom/ElementTraversal`. (9认同)
  • 如果你得到java.lang.NoClassDefFoundError:org/w3c/dom/ElementTraversal尝试将xml-apis 1.4.01添加到你的pom(并排除所有其他依赖版本) (2认同)
  • ElementTraversal 是 Xerces 11 中添加的新类,可在 xml-apis:xml-apis:1.4.01 依赖项中使用。因此,您可能需要手动将类复制到项目中或使用整个依赖项,这会导致类加载器中出现重复的类。但在 JDK9 中包含了该类,因此在功能中您可能需要删除 dep。 (2认同)

Tra*_*ger 42

您可以将maven enforcer插件与禁止的依赖关系规则一起使用.这将允许您禁止所有您不想要的别名,并且只允许您想要的那些别名.违反时,这些规则将使项目的maven构建失败.此外,如果此规则适用于企业中的所有项目,则可以将插件配置放在公司父pom中.

看到:


net*_*key 29

我知道这并没有完全回答这个问题,但对于来自谷歌的ppl来说碰巧使用Gradle进行依赖管理:

我设法摆脱Gradle的所有xerces/Java8问题,如下所示:

configurations {
    all*.exclude group: 'xml-apis'
    all*.exclude group: 'xerces'
}
Run Code Online (Sandbox Code Playgroud)

  • 很好,有了maven,你需要大约4000行XML才能做到这一点. (31认同)
  • @teknopaul XML 纯粹用于配置。Groovy 是一种高级编程语言。有时,您可能希望使用 XML 的显式性而不是 groovy 的魔力。 (4认同)

Jen*_*der 16

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

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

如果不是,你基本上搞砸了,并且必须使用像OSGI这样的东西,它允许你同时加载不同版本的库.请注意,它基本上用类加载器问题替换jar版本问题......

如果存在这样的版本,您可以使您的存储库为所有类型的依赖项返回该版本.这是一个丑陋的黑客攻击,并且会在你的类路径中多次使用相同的xerces实现,但是比拥有多个不同版本的xerces更好.

您可以将每个依赖项排除在xerces之外,并将一个依赖项添加到要使用的版本中.

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

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

所以把它包起来:这是一团糟,不会改变.

  • 究竟.这就是我写的原因:警告它基本上用类加载器问题替换jar版本问题 (3认同)

Dan*_*iel 6

还有另一个选项尚未在此处探讨:将Maven中的Xerces依赖项声明为可选:

<dependency>
   <groupId>xerces</groupId>
   <artifactId>xercesImpl</artifactId>
   <version>...</version>
   <optional>true</optional>
</dependency>
Run Code Online (Sandbox Code Playgroud)

基本上这样做是为了强制所有家属声明他们的Xerces版本或他们的项目不会编译.如果他们想要覆盖这种依赖关系,欢迎他们这样做,但他们将拥有潜在的问题.

这为下游项目创造了强大的动力:

  • 做出积极的决定.他们使用相同版本的Xerces还是使用别的东西?
  • 实际上测试他们的解析(例如通过单元测试)和类加载以及不弄乱他们的类路径.

并非所有开发人员都会跟踪新引入的依赖项(例如mvn dependency:tree).这种方法将立即引起他们的注意.

它在我们的组织中运作良好.在它介绍之前,我们过去常常和OP描述的地狱生活在一起.

  • @chrisinmtown真正的版本. (3认同)

Der*_*ett 6

您应该先进行调试,以帮助确定您的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
Run Code Online (Sandbox Code Playgroud)

到命令行。如果可行,则开始排除库。如果不是,则添加

-Djaxp.debug=1
Run Code Online (Sandbox Code Playgroud)

到命令行。


tek*_*aul 5

每个Maven项目都应该根据xerces停止运行,但实际上并非如此。从1.4开始,XML API和Impl就已经成为Java的一部分。无需依赖xerces或XML API,就像说您依赖Java或Swing一样。这是隐式的。

如果我是Maven仓库的老板,我会写一个脚本来递归地删除xerces依赖关系,并写一个自述文件说该仓库需要Java 1.4。

因为它是通过org.apache导入直接引用Xerces而导致的任何实际中断,都需要进行代码修复,以使其达到Java 1.4级别(自2002年以来已完成),或者通过认可的libs在JVM级别提供解决方案,而不是在maven中。