java maven 项目中的库版本冲突

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19659425/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 19:13:04  来源:igfitidea点击:

Conflicting library version in a java maven project

javamavendependenciesconflict

提问by John Papastergiou

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. A common one is methodnotfound exception ( since A's version of http client doesnt use a specific method anymore ).

但是 B 的字节码取决于加载类的不同版本,这会在运行应用程序时导致多个问题。一个常见的方法是 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 ?

在构建时避免此类冲突的一般策略是什么?是否必须手动检查依赖关系树以找出哪些公共库相互冲突?

采纳答案by Todd Patterson

Welcome to maven dependency hell, as it's fondly known. This is a somewhat common problem as projects grow and more external dependencies are introduced.

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

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. Refer to the "dependencyConvergence" config. Also see this SO post.

我同意“matts”给出的关于如何在确定冲突后解决冲突的建议。在早期捕获这些版本冲突方面,您还可以使用maven“enforcer”插件。请参阅“dependencyConvergence”配置。另请参阅此 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. 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 (e.g. Spring MVC) it can get nasty.

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

Hopefully that's useful information.

希望这是有用的信息。

回答by matts

You can use the treegoalof the Maven dependency plugin to display all transitive dependencies in your project and look for dependencies that say "omitted for conflict".1

您可以使用Maven 依赖项插件的tree目标来显示项目中的所有传递依赖项,并查找显示“因冲突而省略”的依赖项。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 includesparameter 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 和 A 引入不同版本的 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)

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 dependencyManagementsection of your pom and try changing the version until one works.

要真正解决冲突,在某些情况下,可能会找到两个主要依赖项都可以使用的传递依赖项的版本。将传递依赖添加到dependencyManagement你的 pom 部分并尝试更改版本,直到一个有效。

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. Assume C 1.0 and 2.0 are completely incompatible. 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.

但是,在其他情况下,可能无法找到适合所有人的依赖项版本。在这些情况下,您可能必须退回主要依赖项之一的版本,以使其使用适用于每个人的传递依赖项的版本。例如,在上面的示例中,A 0.1 使用 C 1.0,B 0.2 使用 C 2.0。假设 C 1.0 和 2.0 完全不兼容。但也许您的项目有可能改用 B 0.1,这恰好取决于与 C 1.0 兼容的 C 1.5。

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.

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

回答by carlspring

I would like to extend Todd's and Matts' answers with the fact that you can:

我想扩展 Todd 和 Matts 的答案,因为您可以:

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

  • Add an <exclusions/>tag for all your dependencies which have a transitive dependency of project-c.

  • Or, alternatively, inside your project, define explicitly project-cas a dependency in order to override the transitive ones and avoid conflict. (This will still show in your tree when using `-Dverbose).

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

  • <exclusions/>为所有具有传递依赖项的依赖项添加标签project-c

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

Alternatively, if those projects are under your control, you can simply upgrade the version of project-c.

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

回答by Ga???

You could use the maven-enforcer-plugin in your pom to force specific versions of the transitive dependencies. This would help you prevent omissions by the pom configuration when there are conflicts.

您可以在 pom 中使用 maven-enforcer-plugin 来强制传递依赖项的特定版本。这将帮助您在发生冲突时防止 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>