文章目录
- 概述
- 最短路径优先原则
- POM 文件中声明顺序优先原则
- 覆盖优先原则
- MAVEN 依赖冲突常见报错
- Maven 依赖加载流程
概述
Maven 是一个流行的构建工具,用于管理和构建Java项目。它遵循一些核心原则,以确保项目的构建和依赖管理能够高效、一致地进行。以下是您提到的三大原则的解释:
最短路径优先原则
在Maven中,最短路径优先原则指的是当解决项目依赖关系时,Maven会尽量选择最短路径来满足这些依赖关系。这意味着当有多个不同版本的依赖项可供选择时,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 版本
POM 文件中声明顺序优先原则
POM(Project Object Model)是Maven项目的核心配置文件,其中定义了项目的依赖、插件、构建配置等信息。POM 文件中声明顺序优先原则强调了在POM 文件中元素的声明顺序对于项目的继承和覆盖机制的影响。具体来说,子项目的POM 文件会继承父项目的POM 配置,但是如果子项目在自己的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 版本
覆盖优先原则
覆盖优先原则是指当一个项目依赖于多个版本不同的相同依赖项时,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
这些原则有助于确保Maven项目的依赖管理和构建过程具有可预测性、一致性,并且能够最大程度地避免依赖冲突等问题。
MAVEN 依赖冲突常见报错
ClassNotFoundException
当项目启动时出现 ClassNotFoundException 这样的错误,表示由于项目使用的包版本下找不到当前需要的类
1、调用 class 的 forName 方法时,找不到指定的类。
2、 ClassLoader 中的 findSystemClass() 方法时,找不到指定的类。
3、 ClassLoader 中的 loadClass() 方法时,找不到指定的类。
NoSuchMethodError
NoSuchMethodError 就是程序在运行中找不到运行的方法导致的
1、有可能发生的就是 jar 冲突,可能是两个高低版本的 jar 包导致。
2、有可能是有两个 jar 包有相同的类与方法,导致程序调用过程中找不到正确的方法。
Maven 依赖加载流程
Maven 依赖加载流程如下
-
首先,将 parent 的直接依赖,间接依赖,还有依赖管理,插入本项目,放入本项目的直接依赖,间接依赖还有依赖管理之前。
-
对于直接依赖,如果有 version,那么就依次放入 DependencyMap 中。如果没有 version ,则从依赖管理中查出来 version,之后放入 DependencyMap 中。 key 为依赖的 groupId + artifactId, value 为 version ,后放入的会把之前放入的相同 key 的 value 替换。
-
对于每个依赖,各自按照步骤 1 和 2 加载自己的 pom 文件,但是如果第一步中的本项目 dependency management 中有依赖的版本,使用本项目 dependency management 的依赖版本,生成 TransitiveDependencyMap ,这里面就包含了所有的间接依赖。
-
所有间接依赖的 TransitiveDependencyMap , 对于项目的 DependencyMap 里面没有的 key ,依次放入项目的 DependencyMap 。
-
如果 TransitiveDependencyMap 里面还有间接依赖,那么递归执行步骤 3 和 4 。
由于是先放入本项目的 DependencyMap ,再去递归 TransitiveDependencyMap ,这就解释了 Maven 依赖的最短路径原则
参考: MAVEN依赖的优先原则