Java Maven项目中冲突的库版本

在构build具有许多依赖项的Maven项目时,其中一些依赖项依赖于相同的库,但使用不同的版本,这在运行应用程序时会导致错误。

例如,如果我添加两个不同的项目依赖关系,A和B都依赖于apache公共http客户端,但每个都在不同的版本上,一旦类加载器加载A的apache公共http客户端类,B将尝试使用它们它们已经被类加载器加载了。

但是B的字节码依赖于加载类的不同版本,在运行应用程序时会导致多个问题。 常见的一种是methodnotfoundexception(因为A的http客户端版本不再使用特定的方法)。

什么是避免这种冲突build设的总体策略? 是否必须手动检查依赖关系树来找出哪些公共库互相混淆?

欢迎来到maven依赖地狱 ,因为它是很有名的。 随着项目的增长和更多的外部依赖被引入,这是一个比较常见的问题。

除了Apache Commons(在您的原始问题中提到)之外,日志框架(log4j,slf4j)是另一个常见的罪魁祸首。

我同意“地垫”给出的build议,一旦发现冲突,如何解决冲突。 在早期捕获这些版本冲突方面,您也可以使用Maven的“执行者”插件。 请参阅“dependencyConvergence”configuration 。 也看到这个SOpost 。

使用执行者插件会在版本冲突时立即失败,从而避免了手动检查。 这是一个积极的策略,但是可以防止引起问题/发布的运行时问题的types。 像任何事情一样,执法者插件有优点和缺点。 我们在去年开始使用它,但后来发现它可能是一个祝福和诅咒。 libs / frameworks的许多版本是向后兼容的,所以在编译时和运行时,版本1.2.3和1.2.4的版本(直接或间接)都是很好的。 但是,执行者插件将标记此冲突,并要求您声明所需的版本。 假设依赖冲突的数量很less,这不需要太多的工作。 然而,一旦你引入了一个大的框架(例如Spring MVC),它就会变得讨厌。

希望这是有用的信息。

您可以使用Maven依赖项插件的tree目标来显示项目中的所有传递依赖项,并查找说明“为冲突而忽略”的依赖项。 1

 mvn dependency:tree -Dverbose mvn dependency:tree -Dverbose | grep 'omitted for conflict' 

一旦知道哪个依赖关系存在版本冲突,就可以使用includes参数来显示依赖关系,从而导致依赖关系看到特定的依赖关系是如何被拉入的。例如,一个不同版本的C被A拉入的项目和B:

 mvn dependency:tree -Dverbose -Dincludes=project-c [INFO] com.my-company:my-project:jar:1.0-SNAPSHOT [INFO] +- project-a:project-a:jar:0.1:compile [INFO] | \- project-c:project-c:jar:1.0:compile [INFO] \- project-b:project-b:jar:0.2:compile [INFO] \- project-x:project-x:jar:0.1:compile [INFO] \- (project-c:project-c:jar:2.0:compile - omitted for conflict) 

为了真正解决冲突,在某些情况下,可能会find一个版本的传递依赖关系,这两个主要的依赖关系都可以使用。 添加传递依赖到你的pom的dependencyManagement部分,并尝试更改版本,直到一个工程。

但是,在其他情况下,可能无法find适用于所有人的依赖版本。 在这些情况下,您可能必须退回其中一个主要依赖项的版本,才能使用适用于每个人的传递依赖项版本。 例如,在上面的例子中,A 0.1使用C 1.0,B 0.2使用C 2.0。 假设C 1.0和2.0是完全不兼容的。 但也许有可能您的项目使用B 0.1,而恰恰取决于与C 1.0兼容的C 1.5。

当然,这两种策略并不总是奏效,但是我之前已经find了成功的方法。 其他更激进的select包括打包自己的版本的依赖关系,以解决不兼容问题,或试图在单独的类加载器中隔离两个依赖项。

我想延长托德和马茨的答案,事实上你可以:

  • mvn dependency:tree -Dverbose -Dincludes=project-c

  • 为所有依赖项添加一个<exclusions/>标签,这些依赖项具有project-c的传递依赖性。

  • 或者,或者在你的项目中,明确的定义project-c作为一个依赖,以覆盖可传递的并避免冲突。 (使用`-Dverbose时,这仍然会显示在你的树中)。

或者,如果这些项目在您的控制之下,您可以简单地升级project-c的版本。

你可以在你的pom中使用maven-enforcer-plugin来强制传递依赖的特定版本。 这将有助于防止发生冲突时通过pomconfiguration的遗漏。

这是为我工作,我能够改变版本匹配。 如果您无法更改版本,那么这将不会很有帮助。

依赖性收敛

 <project> ... <build> <plugins> ... <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-enforcer-plugin</artifactId> <version>1.4</version> <executions> <execution> <id>enforce</id> <configuration> <rules> <dependencyConvergence/> </rules> </configuration> <goals> <goal>enforce</goal> </goals> </execution> </executions> </plugin> ... </plugins> </build> ... </project> 

使用括号强制依赖关系的版本:

 <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <scope>compile</scope> <version>[1.0.0]</version> </dependency>