Maven依赖解析(冲突)

90
假设我有四个项目:
  • Project A(依赖于B和D)
  • Project B(依赖于D)
  • Project C(依赖于D)
  • Project D
在这种情况下,如果我运行Project A,则Maven将正确解析对D的依赖关系。如果我理解得正确,那么Maven始终选择最短路径的依赖项。由于D是A的直接依赖项,因此将使用该依赖项,而不是在B中指定的D。
但是现在假设这个结构:
  • Project A(依赖于B和C)
  • Project B(依赖于D)
  • Project C(依赖于D)
  • Project D
在这种情况下,解决D的路径具有相同的深度。发生冲突时,Maven将出现冲突。我知道可以告诉Maven排除依赖项。但是我的问题是如何解决这种问题。我的意思是,在真实的应用程序中,您可能有很多依赖项,还可能存在很多冲突。
最佳实践解决方案真的是排除一切吗?还是有其他可能的解决方法?当版本更改导致Maven选择了不同的依赖项时,突然出现ClassNotFound异常让我很难处理。当然,知道这个事实会使猜测问题是依赖关系冲突变得更容易。
我正在使用maven 2.1-SNAPSHOT。

2
我认为你不应该使用2.1-SNAPSHOT版本,因为它是一款DEV版本。Maven 2的最终版本是2.2.1。 - reef
4
顺便提一下,Maven 3.x 已经稳定运行了一段时间,比 Maven 2.x 更快、更可靠。 - Sean Patrick Floyd
3
钻石是程序员的死敌... - Tony Lâmpada
@Sean Patrick Floyd 所有的 Maven 版本都比 SNAPSHOT 更稳定 :) - MariuszS
5个回答

111

在这种情况下,解决问题的Maven方式是在项目的根POM中包含<dependencyManagement>部分,在那里您可以指定将使用哪个库的哪个版本。

编辑:

<dependencyManagement>
  <dependencies>
    <dependency>
        <groupId>foo</groupId>
        <artifactId>bar</artifactId>
        <version>1.2.3</version>
    </dependency>
   </dependencies>
</dependencyManagement>

现在无论哪个版本的库 foo:bar 被依赖请求,版本 1.2.3 都将用于该项目和所有子项目。

参考资料:


1
如果项目 D 的版本不同, 项目 B(依赖于 D1.0)- 无法与 D2.0 兼容 项目 C(依赖于 D2.0)- 无法与 D1.0 兼容 - Nitul
4
如果是这样的话,那你基本上没有办法了。你唯一的选择就是使用类似 Maven Shade 插件这样的工具重新打包其中一个依赖项。 - Sean Patrick Floyd
7
澄清一下,在“dependencyManagement”中指定的版本号适用于传递依赖项和没有显式版本的项目依赖项。但是,具有显式版本的项目依赖项将覆盖“dependencyManagement”部分中的版本。 - pnewhook
我必须将以下内容添加到dependencyManagement中 <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> - anand

39
Maven可以处理这两种情况而不会产生冲突。当需要两个版本的传递依赖时,就会出现冲突。你所描述的ClassNotFoundException结果是应用程序(或依赖项)尝试使用在实际使用的冲突依赖版本中不可用的类。
有多种方法可以解决这个问题:
  1. 更新使用冲突依赖的库的版本,以使它们都依赖于该依赖的相同版本
  2. 将冲突依赖声明为项目的直接依赖项,并包括您想要包含的版本(在本例中,包含缺少类的那个版本)
  3. 通过POM文件的<dependencyManagement>部分指定传递依赖项应使用哪个版本的冲突依赖项
  4. 通过<exclusion>显式地排除不需要的冲突依赖项版本,避免它们被包括在依赖项中

这个答案帮助我解决了构建错误。但是,这是以不再依赖支持和测试的配置为代价吗?如果您使用<dependencyManagement>强制一个模块使用其不想要的依赖版本,则可能会暴露潜在的错误。为什么Java世界不能更严格地遵守语义化版本规则呢?然后构建工具可以使用版本范围而不是特定版本,这样就可以减少需要解决的冲突。也许? - BobHy

28

这并不是一个Maven的问题,而是Java的问题。

如果项目B和项目C需要项目D的两个不兼容版本,则无法在项目A中同时使用它们。

Maven解决这类冲突的方式很不幸,正如您已经知道的那样,是选择排除哪些版本。

使用mvn dependency:analyzemvn dependency:tree有助于找出您有哪些冲突。


我认为不向后兼容的构件应该更改它们的groupId和/或artifactId。 - Sean Patrick Floyd
1
是的,但这只能帮你完成一半。这些构件不能包含具有相同包和名称的类。 - Buhb
是的,不幸的是。在这种情况下,您需要使用ProGuard或Maven Shade插件。 - Sean Patrick Floyd
OSGI能解决这个假设的场景吗?(并不是说这是一个简单的解决方案 - 只是在问。) - jhericks
可能吧。我不太了解OSGI,不能确定地说什么。 - Buhb

24
你可以使用规则Dependency Convergence在整个项目中enforce强制一致的依赖关系。
 <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-enforcer-plugin</artifactId>
     <version>1.3.1</version>
     <executions>
        <execution>
           <id>enforce</id>
           <configuration>
              <rules>
                 <DependencyConvergence/>
              </rules>
           </configuration>
           <goals>
              <goal>enforce</goal>
           </goals>
        </execution>
     </executions>
  </plugin>

9

一种可能的策略是为主项目指定要使用的D版本(例如最新版本)。但是,如果库D不向后兼容,则存在问题,正如kukudas所述-无法在项目中同时使用两个库。

在这种情况下,可能需要使用B或C的旧版本,以便两者都依赖于兼容的D版本。


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