java maven项目中的库版本冲突
在构建具有多个依赖项的maven项目时,其中一些依赖项依赖于同一个库,但使用不同的版本,这会在运行应用程序时导致错误 例如,如果我添加了两个不同的项目依赖项,A和B都依赖于apache commons http客户端,但每个依赖项都在不同的版本上,那么一旦类加载器加载了A的apache commons http客户端类,B就会尝试使用它们,因为类加载器已经加载了它们 但是B的字节码取决于加载类的不同版本,在运行应用程序时会导致多个问题。一个常见的异常是MethodNotFoundException(因为A的http客户端版本不再使用特定的方法) 为避免此类冲突而构建时的总体策略是什么?您是否必须手动检查依赖关系树,以确定哪些公共库相互关联?您可以使用Maven依赖关系插件的来显示项目中所有可传递的依赖关系,并查找显示“因冲突而忽略”的依赖关系 一旦知道哪个依赖项存在版本冲突,就可以使用java maven项目中的库版本冲突,java,maven,dependencies,conflict,Java,Maven,Dependencies,Conflict,在构建具有多个依赖项的maven项目时,其中一些依赖项依赖于同一个库,但使用不同的版本,这会在运行应用程序时导致错误 例如,如果我添加了两个不同的项目依赖项,A和B都依赖于apache commons http客户端,但每个依赖项都在不同的版本上,那么一旦类加载器加载了A的apache commons http客户端类,B就会尝试使用它们,因为类加载器已经加载了它们 但是B的字节码取决于加载类的不同版本,在运行应用程序时会导致多个问题。一个常见的异常是MethodNotFoundExceptio
includes
参数来显示导致该依赖项的依赖项,以查看特定依赖项是如何被拉入的。例如,a和B引入不同版本C的项目:
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的dependencManagement
部分,并尝试更改版本,直到一个版本起作用
但是,在其他情况下,可能无法找到适用于所有人的依赖关系版本。在这些情况下,您可能必须退回其中一个主要依赖项的版本,以使其使用适用于所有人的可传递依赖项的版本。例如,在上面的示例中,A 0.1使用C1.0,B 0.2使用C2.0。假设C1.0和2.0完全不兼容。但是,也许您的项目可以使用b0.1,而b0.1恰好依赖于c1.5,c1.5与c1.0兼容
当然,这两种策略并不总是有效的,但我以前发现过它们的成功之处。其他更激烈的选择包括打包您自己版本的依赖项以修复不兼容性,或者尝试在单独的类加载器中隔离这两个依赖项。欢迎使用,众所周知。随着项目的发展和更多的外部依赖关系的引入,这是一个比较常见的问题
除了Apache Commons(在您最初的问题中提到),日志框架(log4j、slf4j)是另一个常见的罪魁祸首
我同意“马特”提出的建议,即一旦发现冲突,如何解决冲突。为了尽早发现这些版本冲突,您还可以使用maven“enforcer”插件。请参阅。另见
使用enforcer插件将在版本冲突时立即使构建失败,从而避免手动检查。这是一种积极的策略,但可以防止引发问题/帖子的运行时问题。像任何东西一样,enforcer插件也有其优点和缺点。我们在去年开始使用它,但后来发现它可以是一种祝福也可以是一种诅咒。libs/frameworks的许多版本都是向后兼容的,因此(直接或间接地)依赖于1.2.3版和1.2.4版通常在编译时和运行时都可以。但是,enforcer插件将标记此冲突,并要求您准确声明所需的版本。假设依赖冲突的数量很小,这不需要做很多工作。然而,一旦你引入了一个大型框架(例如SpringMVC),它就会变得很糟糕
希望这是有用的信息。我想扩展Todd和Matts的答案,您可以:
mvn依赖项:tree-Dverbose-Dincludes=project-c
- 为所有依赖项添加一个
标记,这些依赖项的传递依赖项为project-c
- 或者,在项目内部,将
明确定义为依赖项,以覆盖可传递的依赖项并避免冲突。(当使用“-Dverbose”时,这仍将显示在树中)project-c
或者,如果这些项目在您的控制之下,您可以简单地升级
project-c
的版本。您可以使用pom中的maven enforcer插件强制转换依赖项的特定版本。这将有助于防止pom配置在发生冲突时出现遗漏
这对我来说很有效,我能够更改版本以匹配。如果您无法更改版本,那么这将没有多大帮助
...
...
org.apache.maven.plugins
maven enforcer插件
1.4
执行
执行
...
...
使用括号强制依赖项上的版本:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>compile</scope>
<version>[1.0.0]</version>
</dependency>
org.slf4j
slf4j api
编译
[1.0.0]
我的情况似乎是一种无法满足所有人的依赖关系。这主要是因为我将遗留软件(遗憾的是,它的源代码没有提供给我)与共享公共依赖项的较新库集成在一起。总而言之,最好的解决方案是在编译时或之前获得错误和警告,以表明我需要做一些手工工作。至少它让我省去了部署和解决冲突的麻烦。enforcer插件似乎很好地实现了这一点
<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>