Maven 依赖可以分为如下几部分:
PS:bom 就是工程项目中最外层主 POM ,也就是 dependencyManagement 那个 POM
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 版本
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 版本
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);
}
}
当项目启动时出现 ClassNotFoundException 这样的错误,表示由于项目使用的包版本下找不到当前需要的类 1、调用 class 的 forName 方法时,找不到指定的类。2、 ClassLoader 中的 findSystemClass() 方法时,找不到指定的类。3、 ClassLoader 中的 loadClass() 方法时,找不到指定的类。
NoSuchMethodError 就是程序在运行中找不到运行的方法导致的 1、有可能发生的就是 jar 冲突,可能是两个高低版本的 jar 包导致。2、有可能是有两个 jar 包有相同的类与方法,导致程序调用过程中找不到正确的方法。
Maven 依赖加载流程如下
由于是先放入本项目的 DependencyMap ,再去递归 TransitiveDependencyMap ,这就解释了 Maven 依赖的最短路径原则。可用文中 1.1 做示例如下:
本次主要讲解了 maven 依赖包含的几大部分,以及 maven 依赖三大原则,并且对每种依赖原则都做了具体图解和示例,方便我们可以直接在项目中运行调试。期间对 maven 冲突导致的常见报错进行讲解方便我们在开发过程中快速定位问题。最后讲解了 maven 加载包进入项目中的整个流程,并按照文中 1.1 所讲的“最短路径优先原则“做为示例图解,进而加深对 maven 加载包流程的理解。