【问题标题】:How java/maven resolves dependency conflicts at run timejava/maven 如何在运行时解决依赖冲突
【发布时间】:2017-11-20 14:53:36
【问题描述】:

对不起,我的新手问题。

假设我有一个包 A,例如,它在其 maven 文件中声明 B、C 作为其依赖项。 B、C 使用两个不同版本的 log4j 进行日志记录。我有几个问题:

  1. 如果我使用 maven,并将 B、C 声明为 A 的依赖项。当 maven 从 mavencentral repo 拉入 B、C 的工件 (.jar) 时。 B、C jar 文件是否包含 log4j 类文件或仅包含它们自己的编译文件(B、C 自己的源代码,而不是依赖项)。
  2. 如果我理解正确的话,当构建发生时,最终构建中只会有一个 log4j 类文件(即使 B、C 使用不同版本的 log4j)。这里要选择哪个版本的 log4j 构建?这是否意味着我也需要将 log4j 声明为 A 依赖项(在 A 的 maven 构建文件中) - 并且该版本将被选择构建版本。
  3. B、C 可能使用完全不同的 log4j 版本。那里的 API 可能完全不同。它应该在运行时引起问题吗?但在现实中,它非常罕见吗?为什么这样?

谢谢。

【问题讨论】:

  • Maven 假设总是可以使用新版本的工件而不是旧版本。如果没有,您需要开始在依赖项中使用 exclude。

标签: java maven


【解决方案1】:
  1. jar 文件通常不包含它们的依赖项。有一种方法可以做到这一点,称为 fat jar。 What is a fat JAR? 但假设您使用的是常规 jar 依赖项。 jar 只会在自己的 pom.xml 中声明它们的依赖关系。因此,对于您的示例,B 和 C 将只包含它们自己编译的源代码。

  2. 这实际上取决于您如何打包文件。一般来说,如果你只生成一个简单的 jar,它不会包含依赖项,并且提供正确的依赖项是 runner 的责任。例如,在战争的情况下,maven 将抛出所有依赖项。前面提到的另一种方法是肥罐。一种更常见的方法是压缩所有依赖项并单独提供它们。

  3. 我不知道为什么你以前没有遇到过冲突,我遇到过很多其他库,但我不记得 log4j 的案例。作为库维护者,处理此类冲突的一种方法是,当您进行非向后兼容的更改时,更改包名称,这样用户可以安全地在类路径中拥有多个版本(无论如何都应该避免)。

Maven 有办法避免这种冲突,它会优先考虑最接近定义的依赖版本。例如,如果您在 A 中声明了版本 a,在 B 中声明了版本 b,那么有效版本将为 A。

此外,还有一些其他机制,例如依赖关系管理。你可以看这里:https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html

这个话题非常严重,可能会导致很多难以检测的生产错误。希望这会有所帮助...

【讨论】:

    【解决方案2】:
    1. 它们应该只包含自己的类。不是他们依赖的类。您可以打开jar文件并自己查看。 Jar 文件只是 zip 文件。
    2. Maven 将通过选择最接近依赖关系树根的版本来解决冲突。如果两个版本在依赖树中的深度相同,则选择第一个版本(IIRC)。如果 A 本身依赖于 log4j,或者如果您希望在运行时使用特定版本,则应将 log4j 指定为 A 的直接依赖项,并使用您想要的版本。或者至少在构建的 dependencyManagement 部分中指定它。
    3. 因为像 Log4J 这样流行的库都力求拥有一个非常稳定的 API,因此不会破坏针对旧版本库编译的代码。

    【讨论】:

    • 反例:谷歌番石榴,新版本删除了旧功能。
    • guava 违反了任何现有的版本编号模式。他们只是随心所欲地玩。买不买……
    【解决方案3】:
    1. 人工制品通常不包含其依赖项(但有一些打包选项可以)。
    2. Maven 将使用一些规则(我不记得详细)仅确定一个版本。如果由于某种原因你必须覆盖它,你可以在 POM 中加入一个依赖管理部分。
    3. 是的,这可能会导致问题。只有在更改公共 API 时小心谨慎,才能避免这些问题。

    【讨论】:

      【解决方案4】:
      1. 如果将 log4j 指定为 B 和 C 的依赖项,并且您不使用创建 uber-jars/fat-jars 的特殊插件,则 B 和 C 都不会包含 log4j 类文件。

      2. 具有相同坐标(groupId、artifactId)的一个依赖项。正如这里有人已经提到的那样,版本通常是通过最短路径选择到根目录的。所以如果你想使用特定的 log4j 版本,你可以在你的 pom 中指定它。

      3. 如果您以标准方式使用 log4j,即仅指定配置文件,则两个版本(log4j 和 log4j2)通常可以共存,因为它们使用不同的包和不同的配置文件。只需查看log4j的迁移站点:Migration from log4j to log4j2

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-02
        • 2019-01-01
        • 2019-08-11
        相关资源
        最近更新 更多