Java/Maven运行时如何解决依赖冲突

4

很抱歉我这个菜鸟提出的问题。

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

  1. 如果我使用maven,并将B和C声明为A的依赖项。当maven从mavencentral库拉取B、C的构件(.jar)时,B、C的jar文件是否包含log4j类文件或仅包含它们自己编译的文件(B、C自己的源代码,而不是依赖项)。
  2. 如果我理解正确,在构建结束时,将只有一个log4j类文件(即使B、C使用不同的log4j版本)。在此选择哪个log4j版本来构建?这是否意味着我需要在A的maven构建文件中也声明log4j作为A的依赖项,并选择要构建的版本。
  3. B,C可能使用完全不同的log4j版本。它们的API可能完全不同。这会在运行时引起问题吗?但实际上,这种情况非常罕见?为什么这样说?

谢谢。


Maven假设较新版本的构件始终可以替代旧版本。如果不行,您需要在依赖项中使用排除操作。 - Thorbjørn Ravn Andersen
4个回答

4
  1. jar文件通常不包含它们的依赖项。有一种方法可以做到这一点,叫做fat jar。什么是fat JAR?但是假设你正在使用普通的jar依赖项。这些jar将仅在其自己的pom.xml中声明其依赖项。因此,对于您的示例,B和C将仅包含自己的已编译源代码。

  2. 这取决于你如何打包文件。一般来说,如果你只生成一个简单的jar,它将不包含依赖项,运行时负责提供正确的依赖项。例如,在war的情况下,maven会将所有依赖项都扔进去。如前所述,另一种常见的方法是压缩所有依赖项并单独提供它们。

  3. 我不知道为什么你以前没有遇到过冲突,我曾经与许多其他库遇到过,虽然我不记得有log4j的情况。作为库维护者处理这些冲突的一种方式是,在进行不向后兼容的更改时更改包名称,这样用户就可以安全地在类路径中拥有多个版本(无论如何应该避免这种情况)。

Maven有一种方式可以避免这些冲突,它将优先考虑最近定义的依赖项版本。例如,如果在A中声明了版本a,在B中声明了版本b,则有效版本将是A。

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

这个主题非常严肃,可能会导致许多难以检测的生产错误。希望这可以帮助到您...


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

反例:Google Guava,其中较新版本已删除了旧功能。 - Thorbjørn Ravn Andersen
Guava 违反了任何已存在的版本编号模式。他们随心所欲地玩耍。你要么买它,要么不买。 - khmarbaise

0
  1. 一个工件通常不包含它的依赖项(但有一些打包选项可以包含)。
  2. Maven将使用一些规则确定仅一个版本(我无法详细回忆)。如果您因某种原因必须覆盖它,可以将依赖管理部分放入POM中。
  3. 是的,这可能会导致问题。只有小心更改公共API时才能避免这些问题。

0
  1. 如果将log4j指定为B和C的依赖项,并且您不使用创建超级JAR / fat-jar的特殊插件,则B和C都不会包含log4j类文件。

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接