maven依赖的优先原则

Maven 依赖可以分为如下几部分:

  1. 直接依赖,就是本项目 dependencies 部分的依赖
  2. 间接依赖,就是本项目 dependencies 部分的依赖所包含的依赖
  3. 依赖管理,就是本项目 dependency management 里面的依赖
  4. parent 的直接依赖
  5. parent 的间接依赖
  6. parent 的依赖管理
  7. bom 的直接依赖(一般没有)
  8. bom 的间接依赖(一般没有)
  9. bom 的依赖管理

PS: bom 就是工程项目中最外层主 POM ,也就是 dependencyManagement 那个 POM

一、 MAVEN 依赖三大原则

1.1 最短路径优先原则

Maven 依赖遵循最短路径优先原则,当项目直接依赖一个 C-api-1.0 和 A-api-2.1 包,并且 C-api-1.0 有如下间接依赖关系: C-api-1.0 —> B-api-1.0 —> A-api-1.1 这时候项目里包含了 A-api 的 1.1 和 2.1 两个版本,由于存在最短路径原则明显 Project —> A-api-2.1 短于 Project —> C-api-1.0 —> B-api-1.0 —> A-api-1.1 故 Project 项目里会使用 A-api-2.1

示例项目中包含如下依赖:

<dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi-ooxml</artifactId>
      <version>3.10-FINAL</version>
    </dependency>

    <dependency>
      <artifactId>QLExpress</artifactId>
      <groupId>com.alibaba</groupId>
      <version>3.2.2</version>
    </dependency>

</dependencies>

其中 poi-ooxml 存在如下依赖关系: poi-ooxml-->poi-->commons-logging (版本 1.1 ) QLExpress 存在如下依赖关系: QLExpress —> commons-logging (版本 1.1.1 ) 由于存在最短路径原则,明显 QLExpress —> commons-logging 路径更短,项目会使用 commons-logging 的 1.1.1 版本

1.2 POM 文件中申明顺序优先原则

Maven 依赖遵循 POM 文件中申明顺序优先原则,当项目里存在直接依赖 C-api-1.0 和 B-api-1.0 其中存在如下间接依赖关系: C-api-1.0 —> A-api-2.1 B-api-1.0 —> A-api-1.1 这时项目间接依赖了 A-api 的 2.1 和 1.1 两个版本,由于存在 POM 文件中申明顺序优先原则,故项目中会使用 A-api-2.1

示例项目中存在如下依赖:

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
  </dependency>


  <dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.10-FINAL</version>
  </dependency>

  <dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-scratchpad</artifactId>
    <version>3.17-beta1</version>
  </dependency>
</dependencies>

其中 poi-ooxml 依赖了包 poi ,poi-scratchpad 也依赖了 poi 包,但是 poi-ooxml 依赖了 poi 包的 3.10-FINAL 版本, poi-scratchpad 依赖了 poi 包的 3.17-beta1 版本,由于存在申明顺序优先原则,项目会使用 poi 包的 3.10-FINAL 版本

1.3 覆盖优先原则

Maven 依赖遵循覆盖优先原则,项目父 POM 中直接依赖包 A-api-1.1 ,子模块 Module A 的 parent 直接依赖了项目的 POM ,但是同时也直接依赖了 A-api-1.2 。 由于存在覆盖优先原则子模块 Module A 中会优先使用 A-api-1.2 而不是父POM的 A-api-1.1

在以上项目工程下,新建一个子工程,在子工程POM添加如下依赖:

<dependencies>
    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi</artifactId>
      <version>5.2.3</version>
    </dependency>
</dependencies>

由于外部工程依赖了 poi 包的 3.10-FINAL ,但是子工程依赖了 poi 的 5.2.3 版本,故整体包依赖会包含poi的两个版本,但是在子工程中使用的是 5.2.3

怎么证明我们在子工程使用的是 poi 的 5.2.3 版本呢? 我们在子工程可以写一个 main 方法调用方法 org.apache.poi.util.Units 类的 columnWidthToEMU 方法,在父工程同样调用这个方法,发现父工程这个方法报错不存在。

import org.apache.poi.util.Units;
public class Test {

    public static void main(String[] args) {
        Units.columnWidthToEMU(1);
    }

}

二、 MAVEN 依赖冲突常见报错

2.1 ClassNotFoundException

当项目启动时出现 ClassNotFoundException 这样的错误,表示由于项目使用的包版本下找不到当前需要的类:

  1. 调用 class 的 forName 方法时,找不到指定的类。
  2. ClassLoader 中的 findSystemClass() 方法时,找不到指定的类。
  3. ClassLoader 中的 loadClass() 方法时,找不到指定的类。

2.2 NoSuchMethodError

NoSuchMethodError 就是程序在运行中找不到运行的方法导致的 1、有可能发生的就是 jar 冲突,可能是两个高低版本的 jar 包导致。 2、有可能是有两个 jar 包有相同的类与方法,导致程序调用过程中找不到正确的方法。

三、Maven 依赖加载流程

Maven 依赖加载流程如下:

  1. 首先,将 parent 的直接依赖,间接依赖,还有依赖管理,插入本项目,放入本项目的直接依赖,间接依赖还有依赖管理之前。
  2. 对于直接依赖,如果有 version,那么就依次放入 DependencyMap 中。如果没有 version ,则从依赖管理中查出来 version,之后放入 DependencyMap 中。 key 为依赖的 groupId + artifactId, value 为 version ,后放入的会把之前放入的相同 key 的 value 替换。
  3. 对于每个依赖,各自按照步骤 1 和 2 加载自己的 pom 文件,但是如果第一步中的本项目 dependency management 中有依赖的版本,使用本项目 dependency management 的依赖版本,生成 TransitiveDependencyMap ,这里面就包含了所有的间接依赖。
  4. 所有间接依赖的 TransitiveDependencyMap , 对于项目的 DependencyMap 里面没有的 key ,依次放入项目的 DependencyMap 。
  5. 如果 TransitiveDependencyMap 里面还有间接依赖,那么递归执行步骤 3 和 4 。

由于是先放入本项目的 DependencyMap ,再去递归 TransitiveDependencyMap ,这就解释了 Maven 依赖的最短路径原则。 可用文中 1.1 做示例如下:

四、总结

本次主要讲解了 maven 依赖包含的几大部分,以及 maven 依赖三大原则,并且对每种依赖原则都做了具体图解和示例,方便我们可以直接在项目中运行调试。期间对 maven 冲突导致的常见报错进行讲解方便我们在开发过程中快速定位问题。最后讲解了 maven 加载包进入项目中的整个流程,并按照文中 1.1 所讲的“最短路径优先原则“做为示例图解,进而加深对 maven 加载包流程的理解。

作者:政采云技术
链接:https://juejin.cn/post/7272340330698014777
来源:稀土掘金

举报
评论 0