首页 文章

java maven项目中的库版本冲突

提问于
浏览 49
41

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

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

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

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

4 回答

  • 37

    欢迎来到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),它就会变得很糟糕 .

    希望这是有用的信息 .

  • 25

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

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

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

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

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

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

    您可以使用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兼容 .

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

相关问题