依赖传递
maven 的依赖, 可以形象的展示为一颗树, 每一个节点都可以引入更多的依赖节点. 那么当依赖树引入了一个包的多个版本, maven 如何决定使用哪一个版本呢? 原理很简单: 最短路径.
如何理解最短路径?
- 层级越少的, 优先级越高.
- 同一层级的, 越靠前, 优先级越高.
例如下图中, 依赖 A 的优先级为 A1>A2>A3>A4
, B 的优先级为 B1>B2
.
Maven 默认处理策略
最短路径优先
Maven 面对 D1 和 D2 时, 会默认选择最短路径的那个 jar 包, 即 D2.E->F->D2 比 A->B->C->D1 路径短 1.
最先声明优先
如果路径一样的话, 举个例子: A->B->C1
, E->F->C2
, 两个依赖路径长度都是 2, 那么就选择最先声明.
- 在第一级, 谁后声明, 使用谁:
- 不在第一级, 谁先声明, 使用谁:
- 依赖最短路径优先原则: 一个项目依赖了两个 jar 包, 其中 A -> B -> C ->X(1.0) , A -> D -> X(2.0). 由于 X(2.0) 路径最短, 所以项目使用的是 X(2.0).
- pom 文件中申明顺序优先: 如果 A -> B -> X(1.0) , A -> C -> X(2.0) 这样的路径长度一样怎么办呢? 这样的情况下, maven 会根据 pom 文件声明的顺序加载, 如果先声明了 B, 后声明了 C, 那最后的依赖就会是 X(1.0).
- 覆写优先: 子 pom 内声明的优先于父 pom 中的依赖.
原文链接:https://blog.csdn.net/qq_42799615/article/details/105585427
如果按照最短路径优先, 路径相同则最先声明的优先的原则, 那么"同一个文件中后声明的优先级比先声明的高"则会看上去矛盾, 这位老哥的 Maven 源码 debug 给我解答了这个问题.
xiaoxi666 说: 从源码可以看到, 如果在同一个 pom 文件内, 声明了两个 groupId 和 artifactId 完全相同的依赖, 则会以最后一个声明的依赖为准. 因为在实现层面, 它们是保存在 Map 中的, 后一个依赖会把前一个依赖覆盖掉. 这也印证了该原则的名称: 同一个文件内声明, 后者覆盖前者.
相关博客链接:
[系列文章]Maven 源码解析: 依赖调解是如何实现的?
系列文章目录 (请务必按照顺序阅读):
- Maven 依赖调解源码解析 (一): 开篇
- Maven 依赖调解源码解析 (二): 如何调试 Maven 源码和插件源码
- Maven 依赖调解源码解析 (三): 传递依赖, 路径最近者优先
- Maven 依赖调解源码解析 (四): 传递依赖, 第一声明者优先
- Maven 依赖调解源码解析 (五): 同一个文件内声明, 后者覆盖前者
- Maven 依赖调解源码解析 (六):dependencyManagement 版本锁定
- Maven 依赖调解源码解析 (七): 总结
依赖关系
Maven 定义了几种依赖关系, 分别是 compile, test, runtime, provided 和 system:
scope | 说明 | 示例 |
---|---|---|
compile | 编译时需要用到该 jar 包 (默认) | commons-logging |
test | 编译 Test 时需要用到该 jar 包 | junit |
runtime | 编译时不需要, 但运行时需要用到 | mysql |
provided | 编译时需要用到, 但运行时由 JDK 或某个服务器提供 | servlet-api |
如果不显式设置 <scope>
属性时, 默认 <scope>compile</scope>
.
Maven 将通过遍历依赖关系图找到所有的依赖关系, 并且构建该应用程序.
可传递性依赖发现
一种相当常见的情况, 比如说 A 依赖于其他库 B. 如果另外一个项目 C 想要使用 A, 那么 C 项目也需要使用库 B.
Maven 可以避免去搜索所有所需库的需求. Maven 通过读取项目文件 (pom.xml), 找出它们项目之间的依赖关系.
我们需要做的只是在每个项目的 pom 中定义好直接的依赖关系, 其他的事情 Maven 会帮我们搞定.
命令
mvn dependency:help
mvn dependency:analyze
mvn dependency:tree
mvn dependency:tree -Dverbose
依赖管理解决什么问题
当同一个工程内有多个模块时, 并且要求多个模块使用某个 jar 包的相同版本, 为了方便统一版本号, 升级版本号, 需要提取出一个父亲模块来管理子模块共同依赖的 jar 包版本.
dependencyManagement 的作用有两个:
- 指定使用某个依赖的哪一个版本
- 统一管理版本
在 dependencyManagement 指定版本号后, 在与 dependencyManagement 同级的, dependencies 标签里面, 引入依赖时, 就可以不指定版本号了.
最佳实践
- 项目中源代码使用的 jar 包一定在 pom.xml 中显式引用.
- 经常 check 一下包冲突, 检查是否需要处理.
- 当使用多个模块时, parent 一定要使用包管理模块来规范 Jar 包版本, 而不是包依赖模块直接引入依赖
文章评论