2017-06-18 87 views
2

对不起,我的新手问题。java/maven如何在运行时解决依赖冲突

假设我有一个包A,它声明B,C作为它的maven文件中的依赖项。 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可能完全不同。它应该在运行时造成问题?但事实上,这是非常罕见的?为什么这样?

谢谢。

+0

Maven有一个假设,可以始终使用较新版本的工件而不是旧版本。如果不是,则需要在依赖项中使用排除开始播放。 –

回答

1
  1. 它们应该只包含自己的类。不是它们的依赖类。你可以打开jar文件并自己看看。 Jar文件只是zip文件。
  2. Maven将通过选择与依赖关系树的根最接近的版本来解决冲突。如果两个版本在依赖关系树中处于相同深度,那么第一个版本会被挑选(IIRC)。如果A本身依赖于log4j,或者如果您希望在运行时使用特定版本,则应该将log4j指定为A的直接依赖关系,并使用所需的版本。或者至少在构建的dependencyManagement部分指定它。
  3. 由于像Log4J一样流行的库努力获得一个非常稳定的API,因此不会破坏针对较旧版本库编译的代码。
+0

计数器示例:google guava,其中较新版本的旧功能已删除。 –

+0

番石榴是违反任何形式的版本编号模式存在..他们只是玩他们喜欢的。要么你买它或不... – khmarbaise

+0

@khmarbaise没有。他们使用语义版本。 –

0
  1. 一个artefact通常不会包含它的依赖关系(但是有包装选项)。
  2. Maven将只使用一些规则确定一个版本(我不记得详细)。如果由于某种原因你必须重写这个,你可以把一个依赖管理部分放到POM中。
  3. 是的,这可能会导致问题。只有在对公共API进行更改时小心才能避免它们。
3
  1. jar文件通常不包含它们的依赖关系。有一种方法可以做到这一点,称为胖罐。 What is a fat JAR?但假设您正在使用常规的jar依赖项。罐子只会在他们自己的pom.xml中声明他们的依赖关系。所以对于你的例子,B和C将只包含他们自己编译的源代码。

  2. 这实际上取决于你如何打包文件。一般来说,如果你只生成一个简单的jar,它将不包含依赖关系,并且运行者有责任提供正确的依赖关系。例如,在战争中,maven会抛出所有的依赖关系。前面提到的另一种方式是胖罐子。另一种常见的方法是压缩所有依赖项并分别提供它们。

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

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

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

这个主题是非常严重的,可能会导致很多检测到生产错误。希望这有助于...

0
  1. 如果log4j的被指定为B和C的依赖,你不使用,创造尤伯杯瓶/脂肪罐,B和C将不包含log4j的类特殊的插件文件。

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

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