java maven项目中的库版本冲突

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

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

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

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

回答(4)

2 years ago

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

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

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

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

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

2 years ago

您可以在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>

2 years ago

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

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

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

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

希望这是有用的信息 .

2 years ago

您可以使用Maven依赖项插件的tree goal来显示项目中的所有传递依赖项,并查找说"omitted for conflict"的依赖项 . 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)

要实际解决冲突,在某些情况下,可能会找到两个主要依赖项都可以使用的传递依赖项的版本 . 将传递依赖项添加到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兼容 .

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