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

示例项目中包含如下依赖

xml 复制代码
<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 示例项目中存在如下依赖

xml 复制代码
<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添加如下依赖

xml 复制代码
<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 方法,在父工程同样调用这个方法,发现父工程这个方法报错不存在。

typescript 复制代码
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 加载包流程的理解。

推荐阅读

ES亿级商品索引拆分实战

在 ARM 环境下搭建原生 Hadoop 集群

利用流量保障搜索质量的实践

Rc-form: 消失的"Ta"

Redis Bigkey 排查

招贤纳士

政采云技术团队(Zero),Base 杭州,一个富有激情和技术匠心精神的成长型团队。规模 500 人左右,在日常业务开发之外,还分别在云原生、区块链、人工智能、低代码平台、中间件、大数据、物料体系、工程平台、性能体验、可视化等领域进行技术探索和实践,推动并落地了一系列的内部技术产品,持续探索技术的新边界。此外,团队还纷纷投身社区建设,目前已经是 google flutter、scikit-learn、Apache Dubbo、Apache Rocketmq、Apache Pulsar、CNCF Dapr、Apache DolphinScheduler、alibaba Seata 等众多优秀开源社区的贡献者。

如果你想改变一直被事折腾,希望开始折腾事;如果你想改变一直被告诫需要多些想法,却无从破局;如果你想改变你有能力去做成那个结果,却不需要你;如果你想改变你想做成的事需要一个团队去支撑,但没你带人的位置;如果你想改变本来悟性不错,但总是有那一层窗户纸的模糊......如果你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的自己。如果你希望参与到随着业务腾飞的过程,亲手推动一个有着深入的业务理解、完善的技术体系、技术创造价值、影响力外溢的技术团队的成长过程,我觉得我们该聊聊。任何时间,等着你写点什么,发给 zcy-tc@cai-inc.com

微信公众号

文章同步发布,政采云技术团队公众号,欢迎关注

相关推荐
爬山算法43 分钟前
Maven(28)如何使用Maven进行依赖解析?
java·maven
duration~16 小时前
Maven随笔
java·maven
狂放不羁霸17 小时前
idea | 搭建 SpringBoot 项目之配置 Maven
spring boot·maven·intellij-idea
雷神乐乐18 小时前
Maven学习——创建Maven的Java和Web工程,并运行在Tomcat上
java·maven
尘浮生1 天前
Java项目实战II基于Spring Boot的光影视频平台(开发文档+数据库+源码)
java·开发语言·数据库·spring boot·后端·maven·intellij-idea
aloha_7891 天前
从零记录搭建一个干净的mybatis环境
java·笔记·spring·spring cloud·maven·mybatis·springboot
尢词1 天前
SpringMVC
java·spring·java-ee·tomcat·maven
wrx繁星点点1 天前
享元模式:高效管理共享对象的设计模式
java·开发语言·spring·设计模式·maven·intellij-idea·享元模式
前 方2 天前
若依入门案例
java·spring boot·maven
咕哧普拉啦2 天前
乐尚代驾十订单支付seata、rabbitmq异步消息、redisson延迟队列
java·spring boot·mysql·spring·maven·乐尚代驾·java最新项目