Maven依赖冲突,何解?

2,679 阅读3分钟

本文涉及到如下几个概念需要注意:依赖冲突、依赖调解、依赖传递。

依赖冲突是什么

原来运行得好好的代码,在引入一个 Jar 包后出现 NoSuchMethodException、NoClassDefFoundError 等错误提示,那么恭喜你,大概率遇到了依赖冲突

在认识依赖冲突之前,我们先来了解一下 Maven 的依赖调解

依赖调解

参考 Maven依赖冲突问题原理简析

简单的说,如果一个项目中引入了同一个依赖(jar 包)的两个(或多个)不同版本,Maven 会对这些依赖做调解,以确保最终(运行时)在项目中对于同一个依赖只引入一个版本。

依赖调解的两个原则:

  1. 路径最近者优先
  2. 第一声明者优先

依赖冲突

现在我们已经知道了,在一个项目中是可能引入同一个依赖(jar 包)的不同版本的。 那么,在同一个 jar 包的不同版本中可能存在一些差异,比如:

  1. 类数量变化,表现形式为类的增加/减少
  2. 类结构变化,表现形式为方法/字段的增加/减少
  3. ......

举个例子,我们项目中引入了两个版本(1.0、2.0)的依赖 A,经过依赖调解后使用了较高的版本(2.0)。如果该依赖在升级的时候没有保持向下的兼容性,删掉了某些方法,而我们项目中使用 1.0 版本的地方刚好调用了被删掉的这个方法,就会出现 NoSuchMethodException 异常。

存在依赖冲突一定意味着异常吗?

不一定。如果经过依赖调解后最终使用的版本能够兼容所有的使用,程序就可以正常运行。

为什么会产生依赖冲突

根本原因是通过直接或者间接依赖,在代码中引入了同一个依赖的不同版本。

怎么解决依赖冲突

  1. 如果能确认高版本向下兼容,那么引入一个高版本 jar 包作为项目的直接依赖(利用路径最近者优先原则)
  2. 用 标签排除掉不需要的依赖。

怎样更快地发现依赖冲突?

在 IDE 中使用 Maven Enforcer Plugin 插件。 这个插件有很多规则,我们最关心的是 Dependency Convergence规则。 用法:

<plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-enforcer-plugin</artifactId>
        <version>1.4.1</version>
        <configuration>
            <rules><dependencyConvergence/></rules>
        </configuration>
    </plugin>
</plugins>

参考官网介绍,该插件有两个执行目标:

  1. enforcer:enforce 用于规则检查
  2. enforcer:display-info 用于显示基本环境信息

执行方式:

  1. 命令行执行 mvn enforcer:enforce
  2. 将该目标绑定到 Maven 的生命周期上自动执行

执行结果:

Dependency convergence error for log4j:log4j:1.2.17 paths to dependency are:
+-com.ricston.conflict:conflict-info:2.1.3-SNAPSHOT
  +-org.slf4j:slf4j-log4j12:1.7.6
    +-log4j:log4j:1.2.17
and
+-com.ricston.conflict:conflict-info:2.1.3-SNAPSHOT
  +-log4j:log4j:1.2.16

根据这种方式发现依赖冲突后,我们就可以使用上面提到的两种方式(排除或者引入可用的直接依赖)来解决冲突。

参考