java maven项目中的库版本冲突

时间:2013-10-29 13:21:46

标签: java maven dependencies conflict

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

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

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

为避免此类冲突而建立的一般策略是什么?是否必须手动检查依赖关系树以确定哪些公共库彼此相互匹配?

4 个答案:

答案 0 :(得分:37)

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

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

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

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

答案 1 :(得分:25)

欢迎来到maven dependency hell,因为它是众所周知的。随着项目的增长和引入更多的外部依赖性,这是一个常见的问题。

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

我同意“matts”给出的关于如何在发现冲突后解决冲突的建议。在早期捕获这些版本冲突方面,您还可以使用maven“enforcer”插件。请参阅"dependencyConvergence" config。另请参阅this SO post

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

希望这是有用的信息。

答案 2 :(得分:2)

您可以在pom中使用maven-enforcer-plugin来强制传递依赖项的特定版本。这可以帮助您在发生冲突时防止pom配置的遗漏。

这对我有用,我能够更改版本以匹配。如果您无法更改版本,那么这将无济于事。

Dependency Convergence

<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>

答案 3 :(得分:1)

我想通过以下事实来扩展Todd和Matts的答案:

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

  • 为所有依赖关系<exclusions/>的依赖项添加project-c标记。

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

或者,如果这些项目在您的控制之下,您只需升级project-c的版本。