在我的办公室里,仅仅提到Xerces这个词就足以煽动开发者的凶悍愤怒。粗略地看一眼其他Xerces关于SO的问题似乎表明,几乎所有Maven用户在某些时候都被这个问题“触动”了。不幸的是,理解这个问题需要对Xerces的历史有一点了解......
xercesImpl.jar
而不是xercesImpl-2.11.0.jar
。xerces.jar
),但被分成两个罐子,一个包含API(xml-apis.jar
),另一个包含那些API(xercesImpl.jar
)的实现。许多较旧的Maven POM仍然声明对xerces.jar
的依赖。在过去的某个时刻,Xerces也被发布为xmlParserAPIs.jar
,一些较老的POM也依赖于它。com.sun.*
命名空间下重新打包,这使得直接访问它们很危险,因为它们可能在某些JRE中不可用。但是,并非所有Xerces功能都通过java.*
和javax.*
API公开;例如,没有API公开Xerces序列化。/lib
文件夹中附带Xerces。对于上述某些原因(或许是全部原因),许多组织在其POM中发布和使用Xerces的自定义构建。如果你有一个小应用程序并且只使用Maven Central,这不是一个真正的问题,但它很快成为企业软件的问题,其中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并不知道它们是同一个神器,因为它们有不同的groupId
s。因此,它无法执行冲突解决,并且jar
s都将作为已解析的依赖项包含在内:
如上所述,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。投票/观看/贡献这个问题,让我们一劳永逸地解决这个问题。
自2013年2月20日起,Maven Central的Xerces有2.11.0 JAR(和源JAR!)!见Xerces in Maven Central。我想知道为什么他们没有解决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
!
什么是最重要的(以及过去不明显的) - Maven Central中的JAR与官方Xerces-J-bin.2.11.0.zip
发行版中的JAR相同。
然而,我无法找到xml-schema-1.1-beta
版本 - 它不能是Maven classifier
版本,因为它有额外的依赖性。
显然xerces:xml-apis:1.4.01
不再是maven中心,然而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>
我的朋友很简单,这里举个例子:
<dependency>
<groupId>xalan</groupId>
<artifactId>xalan</artifactId>
<version>2.7.2</version>
<scope>${my-scope}</scope>
<exclusions>
<exclusion>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
</exclusion>
</exclusions>
</dependency>
如果你想检查你的maven树没有问题的终端(这个例子的Windows控制台):
mvn dependency:tree -Dverbose | grep --color=always '(.* conflict\|^' | less -r
坦率地说,我们遇到的几乎所有东西都适用于JAXP版本,所以我们总是排除xml-apis
和xercesImpl
。
您可以将maven enforcer插件与禁止的依赖关系规则一起使用。这将允许您禁止所有您不想要的别名,并且只允许您想要的那些别名。违反时,这些规则将使项目的maven构建失败。此外,如果此规则适用于企业中的所有项目,则可以将插件配置放在公司父pom中。
看到:
我知道这并没有完全回答这个问题,但是对于那些从谷歌进来的人来说恰好使用Gradle进行依赖管理:
我设法摆脱Gradle的所有xerces / Java8问题,如下所示:
configurations {
all*.exclude group: 'xml-apis'
all*.exclude group: 'xerces'
}
我想你需要回答一个问题:
是否存在xerces * .jar,应用程序中的所有内容都可以使用?
如果不是,你基本上搞砸了,并且必须使用像OSGI这样的东西,它允许你同时加载不同版本的库。请注意,它基本上用类加载器问题替换jar版本问题......
如果存在这样的版本,您可以使您的存储库为所有类型的依赖项返回该版本。这是一个丑陋的黑客攻击,并且会在你的类路径中多次使用相同的xerces实现,但是比拥有多个不同版本的xerces更好。
您可以将每个依赖项排除在xerces之外,并将一个依赖项添加到要使用的版本中。
我想知道你是否可以编写某种版本解析策略作为maven的插件。这可能是最好的解决方案,但如果可行则需要一些研究和编码。
对于运行时环境中包含的版本,您必须确保从应用程序类路径中删除它,或者在考虑服务器的lib文件夹之前首先考虑应用程序jar进行类加载。
所以把它包起来:这是一团糟,不会改变。
还有另一个选项尚未在此处探讨:将Maven中的Xerces依赖项声明为可选:
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>...</version>
<optional>true</optional>
</dependency>
基本上这样做是为了强制所有家属声明他们的Xerces版本或他们的项目不会编译。如果他们想要覆盖这种依赖关系,欢迎他们这样做,但他们将拥有潜在的问题。
这为下游项目创造了强大的动力:
并非所有开发人员都会跟踪新引入的依赖项(例如使用mvn dependency:tree
)。这种方法将立即引起他们的注意。
它在我们的组织中运作良好。在它介绍之前,我们过去常常和OP描述的地狱生活在一起。
您应该先调试,以帮助确定您的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
到命令行。
每个maven项目都应该停止依赖于xerces,它们可能不是真的。自1.4以来,XML API和Impl一直是Java的一部分。没有必要依赖于xerces或XML API,就像说你依赖于Java或Swing。这是隐含的。
如果我是maven repo的老板,我会编写一个脚本来递归删除xerces依赖项,并写一个读取我说这个repo需要Java 1.4。
由于它通过org.apache导入直接引用Xerces而实际中断的任何东西都需要一个代码修复程序,以便将其提升到Java 1.4级别(并且自2002年以来完成)或通过支持的libs而不是maven中的JVM级别的解决方案。
除了排除外,有用的是模块化依赖。
使用一个平面类加载(独立应用程序)或semi-hierarchical (JBoss AS/EAP 5.x)这是一个问题。
但是使用像OSGi和JBoss Modules这样的模块化框架,这不再那么痛苦了。图书馆可以独立使用他们想要的任何图书馆。
当然,最好只推荐一个实现和版本,但如果没有其他办法(使用更多库中的额外功能),那么模块化可能会省去你。
JBoss模块的一个很好的例子当然是JBoss AS 7 / EAP 6 / WildFly 8,它主要是为此开发的。
示例模块定义:
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.jboss.msc">
<main-class name="org.jboss.msc.Version"/>
<properties>
<property name="my.property" value="foo"/>
</properties>
<resources>
<resource-root path="jboss-msc-1.0.1.GA.jar"/>
</resources>
<dependencies>
<module name="javax.api"/>
<module name="org.jboss.logging"/>
<module name="org.jboss.modules"/>
<!-- Optional deps -->
<module name="javax.inject.api" optional="true"/>
<module name="org.jboss.threads" optional="true"/>
</dependencies>
</module>
与OSGi相比,JBoss模块更简单,更快捷。虽然缺少某些功能,但对于大多数(一般)在一个供应商的控制下的项目来说,它足够了,并允许惊人的快速启动(由于并行的依赖关系解决)。
请注意,有一个modularization effort underway for Java 8,但AFAIK主要用于模块化JRE本身,不确定它是否适用于应用程序。