Java Maven 项目中的库版本冲突

2022-09-01 00:59:46

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

例如,如果我添加两个不同的项目依赖项,A和B,它们都依赖于Apache Commons HTTP客户端,但每个都依赖于不同的版本,一旦类加载器加载A的Apache commons http客户端类,B将尝试使用它们,因为它们已经由类加载器加载。

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

在构建以避免此类冲突时,一般策略是什么?是否必须手动检查依赖关系树才能找出哪些公共库相互冲突?


答案 1

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

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

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

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)

若要实际解决冲突,在某些情况下,可能会找到两个主要依赖项都将使用的可传递依赖项版本。将可传递依赖项添加到 pom 的部分,并尝试更改版本,直到一个版本正常工作。dependencyManagement

但是,在其他情况下,可能无法找到适合所有人的依赖项版本。在这些情况下,您可能必须后退一步执行其中一个主要依赖项的版本,以使其使用适用于所有人的可传递依赖项版本。例如,在上面的示例中,A 0.1 使用 C 1.0,B 0.2 使用 C 2.0。假设 C 1.0 和 2.0 完全不兼容。但也许你的项目可以使用B 0.1来代替,这恰好依赖于C 1.5,它与C 1.0兼容。

当然,这两种策略并不总是有效,但我以前已经找到了成功。其他更激烈的选项包括打包您自己的依赖项版本以修复不兼容性,或者尝试在单独的类装入器中隔离这两个依赖项。


答案 2

欢迎来到 maven Dependency hell,因为它被亲切地称为。随着项目的增长和引入更多的外部依赖项,这是一个常见的问题。

除了Apache Commons(在你最初的问题中提到过),日志记录框架(log4j,slf4j)是另一个常见的罪魁祸首。

我同意“马特”关于一旦发现冲突如何解决的建议。在早期捕捉这些版本冲突方面,您还可以使用maven的“执行器”插件。请参阅“依赖关系收敛”配置。另请参阅此SO帖子

使用执行器插件将在版本冲突时立即使构建失败,这使您免于手动检查。这是一种激进的策略,但可以防止提示问题/帖子的运行时问题类型。像任何东西一样,执行器插件有优点和缺点。我们在去年开始使用它,但后来发现它可能是一种祝福,也可能是一种诅咒。许多版本的libs/框架都是向后兼容的,因此(直接或间接地)依赖于版本1.2.3和1.2.4在编译时和运行时通常都很好。但是,执行器插件将标记此冲突,并要求您准确声明所需的版本。假设依赖关系冲突的数量很少,这不需要太多的工作。但是,一旦你引入了一个大框架(例如Spring MVC),它可能会变得很糟糕。

希望这是有用的信息。


推荐