简体   繁体   English

Java Maven项目中的库版本冲突

[英]Conflicting library version in a java maven project

When building a maven project that has many dependencies, some of those dependencies depend on the same library but use a different version which is causing errors when running an application. 在构建具有许多依赖项的Maven项目时,其中一些依赖项依赖于同一库,但使用的版本不同,这会在运行应用程序时导致错误。

For example, if I add two different project dependencies, A and B that both depend on apache commons http client but each one on a different version, once the class-loader loads A's apache commons http client classes, B will try to use them since they are already loaded by the class loader. 例如,如果我添加两个不同的项目依赖项,A和B都依赖于apache commons http客户端,但每个依赖于不同版本,则一旦类加载器加载了A的apache commons http客户端类,B就会尝试使用它们,因为它们已经由类加载器加载。

But B's bytecode depends on a different version of the loaded classes causing multiple problems when running the application. 但是B的字节码取决于所加载类的不同版本,从而在运行应用程序时引起多个问题。 A common one is methodnotfound exception ( since A's version of http client doesnt use a specific method anymore ). 一个常见的方法是methodnotfound异常(因为A的http客户端版本不再使用特定的方法)。

What is the general strategy when building to avoid such conflicts ? 建立避免此类冲突的总体策略是什么? Does one have to manually check the dependency tree to figure out which common libraries colide with each other ? 是否需要手动检查依赖关系树以找出哪些公共库相互冲突?

You can use the tree goal of the Maven dependency plugin to display all transitive dependencies in your project and look for dependencies that say "omitted for conflict". 您可以使用Maven依赖插件的tree目标来显示项目中的所有传递性依赖,并查找说“忽略冲突”的依赖。 1 1个

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

Once you know which dependency has version conflicts, you can use the includes parameter to show just dependencies that lead to that one to see how a particular dependency is being pulled in. For example, a project where different versions of C are pulled in by A and B: 一旦知道哪个依赖项存在版本冲突,就可以使用includes参数仅显示导致该依赖关系的依赖项,以了解如何引入特定的依赖关系。例如,一个项目,其中A引入了不同的C版本和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)

To actually resolve the conflict, in some cases it may be possible to find a version of the transitive dependency that both of your primary dependencies will work with. 为了真正解决冲突,在某些情况下,可能会找到两个主要依赖项都可以使用的传递依赖项的版本。 Add the transitive dependency to the dependencyManagement section of your pom and try changing the version until one works. 将传递性依赖项添加到pom的dependencyManagement部分,然后尝试更改版本,直到可行为止。

However, in other cases it may not be possible to find a version of the dependency that works for everyone. 但是,在其他情况下,可能找不到适合每个人的依赖版本。 In these cases, you may have to step back the version on one of the primary dependencies in order to make it use a version of the transitive dependency that works for everybody. 在这些情况下,您可能必须将版本退回到主要依赖项之一上,以使其使用适用于每个人的可传递依赖项的版本。 For instance, in the example above, A 0.1 uses C 1.0 and B 0.2 uses C 2.0. 例如,在上面的示例中,A 0.1使用C 1.0,而B 0.2使用C 2.0。 Assume C 1.0 and 2.0 are completely incompatible. 假设C 1.0和2.0完全不兼容。 But maybe it is possible for your project to use B 0.1 instead, which happens to depend on C 1.5, which is compatible with C 1.0. 但是,也许您的项目可以改用B 0.1,这恰好取决于C 1.5,它与C 1.0兼容。

Of course these two strategies will not always work, but I have found success with them before. 当然,这两种策略并非总能奏效,但我之前已经找到了成功的方法。 Other more drastic options include packaging your own version of the dependency that fixes the incompatibility or trying to isolate the two dependencies in separate classloaders. 其他更激烈的选择包括打包您自己的依赖关系版本以修复不兼容问题,或尝试将两个依赖关系隔离在单独的类加载器中。

Welcome to maven dependency hell , as it's fondly known. 欢迎来到maven依赖地狱 ,这是众所周知的。 This is a somewhat common problem as projects grow and more external dependencies are introduced. 随着项目的发展以及引入更多的外部依赖关系,这在某种程度上是一个普遍的问题。

Besides Apache Commons (mentioned in your original question), logging frameworks (log4j, slf4j) are another frequent culprit. 除了Apache Commons(在您的原始问题中提到)之外,日志记录框架(log4j,slf4j)是另一个常见的元凶。

I agree with the advice given by "matts" on how to resolve conflicts once they are identified. 我同意“席子”提出的关于一旦解决冲突就如何解决冲突的建议。 In terms of catching these version conflicts early, you can also use the maven "enforcer" plugin. 在及早发现这些版本冲突方面,您还可以使用maven“ enforcer”插件。 Refer to the "dependencyConvergence" config . 请参阅“ dependencyConvergence”配置 Also see this SO post . 另请参阅此SO帖子

Using the enforcer plugin will fail the build immediately on version conflict, which saves you from the manual checks. 使用强制实施程序插件将在版本冲突时立即使构建失败,从而使您免于手动检查。 This is an aggressive strategy, but prevents the type of run-time problems that prompted your question/post. 这是一种激进的策略,但是可以防止提示您问题/帖子的运行时问题类型。 Like anything, the enforcer plugin has pros and cons. 像其他任何东西一样,强制执行插件也各有利弊。 We started using it within the last year, but then discovered it can be a blessing and a curse. 我们在去年开始使用它,但是后来发现它可能是一种祝福和诅咒。 Many versions of libs/frameworks are backwards compatible, and so depending (whether directly or indirectly) on both version 1.2.3 and 1.2.4 is often fine at both compile-time and run-time. 许多版本的lib / frameworks是向后兼容的,因此在编译时和运行时都可以(直接或间接)依赖于1.2.3和1.2.4版本。 However, the enforcer plugin will flag this conflict and require you to declare exactly which version you want. 但是,强制实施程序插件将标记此冲突,并要求您准确声明所需的版本。 Assuming the number of dependency-conflicts is small, this does not require much work. 假设依赖性冲突的数量很少,那么这不需要太多工作。 However, once you introduce a large framework (eg Spring MVC) it can get nasty. 但是,一旦引入了大型框架(例如Spring MVC),它就会变得讨厌。

Hopefully that's useful information. 希望这是有用的信息。

You could use the maven-enforcer-plugin in your pom to force specific versions of the transitive dependencies. 您可以在pom中使用maven-enforcer-plugin强制传递依赖的特定版本。 This would help you prevent omissions by the pom configuration when there are conflicts. 当有冲突时,这将帮助您防止pom配置遗漏。

This is what worked for me, and I was able to change the versions to match. 这是对我有用的,并且我能够更改版本以使其匹配。 If you are unable to change versions, then this won't be very helpful. 如果您无法更改版本,则不会有太大帮助。

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>

Force a version on the dependency using brackets: 使用方括号强制版本依赖项:

<dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <scope>compile</scope>
        <version>[1.0.0]</version>
</dependency>

I would like to extend Todd's and Matts' answers with the fact that you can: 我想通过以下事实来扩展托德和马特斯的答案:

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

  • Add an <exclusions/> tag for all your dependencies which have a transitive dependency of project-c . 为您的所有依赖项添加<exclusions/>标记,这些标记具有可传递的依赖项project-c

  • Or, alternatively, inside your project, define explicitly project-c as a dependency in order to override the transitive ones and avoid conflict. 或者,也可以在项目内部,将project-c显式定义为依赖项,以覆盖可传递的依赖项并避免冲突。 (This will still show in your tree when using `-Dverbose). (使用`-Dverbose时,它仍会显示在树中)。

Alternatively, if those projects are under your control, you can simply upgrade the version of project-c . 或者,如果这些项目在您的控制之下,则只需升级project-c的版本即可。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM