java maven项目中的库版本冲突

java maven项目中的库版本冲突,java,maven,dependencies,conflict,Java,Maven,Dependencies,Conflict,在构建具有多个依赖项的maven项目时,其中一些依赖项依赖于同一个库,但使用不同的版本,这会在运行应用程序时导致错误 例如,如果我添加了两个不同的项目依赖项,A和B都依赖于apache commons http客户端,但每个依赖项都在不同的版本上,那么一旦类加载器加载了A的apache commons http客户端类,B就会尝试使用它们,因为类加载器已经加载了它们 但是B的字节码取决于加载类的不同版本,在运行应用程序时会导致多个问题。一个常见的异常是MethodNotFoundExceptio

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

例如,如果我添加了两个不同的项目依赖项,A和B都依赖于apache commons http客户端,但每个依赖项都在不同的版本上,那么一旦类加载器加载了A的apache commons http客户端类,B就会尝试使用它们,因为类加载器已经加载了它们

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

为避免此类冲突而构建时的总体策略是什么?您是否必须手动检查依赖关系树,以确定哪些公共库相互关联?

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

一旦知道哪个依赖项存在版本冲突,就可以使用
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

  • 或者,在项目内部,将
    project-c
    明确定义为依赖项,以覆盖可传递的依赖项并避免冲突。(当使用“-Dverbose”时,这仍将显示在树中)


或者,如果这些项目在您的控制之下,您可以简单地升级
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>