Maven依赖解析
本文将记录Maven工程中依赖解析机制,内容包括:
- Maven依赖基本结构
- 从仓库解析依赖的机制
- 依赖传递性解析实例
1. Maven依赖基本结构
上篇文章记录了Maven依赖的聚合与继承,POM中依赖的声明通过dependency进行定义,并且通过groupId、artifactId及version三项定位Maven库中的唯一依赖。除了这三项外,还有其他属性进行限制,如下:
<dependencies>
<dependency>
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<type>...</type>
<scope>...</scope>
<optional>...</optional>
<exclusions>
<exclusion>
<groupId>...</groupId>
<artifactId>...</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
- groupId、artifactId及version三项不再叙说;
- type:依赖类型,对应于项目坐标定义的packaging,默认为jar;
- scope:依赖范围,包括compile、test、runtime、import、provided、system;
- optional:标记依赖为可选,即依赖没有传递性;
- exclusions:排除传递性依赖
1.1 依赖范围
我们知道,Maven工程约定具有固定的目录结构,以便于Maven各插件对工程处理,如编译(compile)插件,会将src/main/java中的类编译到target/classes目录下,则编译对应的classpath即为target/classes。依赖范围就是控制依赖于编译classpath(target/classes)、测试classpath(target/test-classes)和运行classpath(以Web工程为例,WEB-INF/classes)的关系。具体依赖范围的讲述可参考官网文档,在此仅进行稍微总结:
- compile:编译依赖范围,对编译、测试、运行classpath都有效,为默认范围;
- test:测试依赖范围,仅对测试classpath有效;
- runtime:运行时依赖范围,对测试和运行classpath有效,对编译无效,如JDBC的依赖引入,因为JDK中值声明了JDBC接口,具体实现由各厂家决定;
- import:导入依赖范围,对三种classpath不产生实际影响,一般是导入pom类型的依赖,聚合情况下需要声明打包类型为pom,其中可包含dependencyManagement(如上一篇文章),此时对引入该依赖的工程不产生影响;
- provided:已提供依赖范围,对测试和编译classpath有效,对运行时无效,如servlet-api在测试或编译的时候需要,在运行的时候由容器提供,故不需要重复引入;
- system:系统依赖范围,与provided范围一致,需要明确指定该jar包,其不在Maven仓库中,感觉不太常用。
1.2 依赖传递性
依赖传递性,举例说明,比如A引用B,B引用C,正常情况下,A也会引用C依赖,即A经过传递间接引用了C(依赖为可选时,需另行处理)。具体不同范围的依赖经过传递后,其依赖范围的变化如下边(从官网扣下来的)

依赖传递的准则:
- 路径最近这优先,即在引用传递链上,获取出离本POM最近的传递性依赖;
- 如果路径距离相同,则以取POM中的声明顺序靠前的。
1.3 可选依赖
可选依赖不被传递。假设项目A依赖于B,B依赖于X和Y,并且X和Y声明为可选,则X和Y对A就不具有传递性了。如果A需要依赖于X或Y,则需要直接引用。
1.4 依赖排除
假设项目A依赖于B,B依赖于C(版本为1.0),此时A会传递性依赖于C(1.0)。如果A需要引用C(2.0版本),存在两种情况:
(1)A直接引用C(2.0),则可以不对B依赖添加exclusions元素;
(2)A在引用B的同时,还引用D(D引用C(2.0)),则可以在引用B的时候使用exclusions元素将C(1.0)排除,也可以将D依赖声明在B之前。
2. 从仓库解析依赖的机制
依赖解析的基本过程:当本地仓库中没有依赖构件,则Maven从远程仓库中下载;当依赖版本为快照版本时,Maven会自动计算最新的快照,并引用。
背后的依赖解析机制概括如下:
(1)当依赖的范围为system,则从本机文件系统中解析构件;
(2)根据依赖坐标计算定位依赖位置后,尝试从本地仓库寻找依赖,若找到,则解析成功;
(3)若本地仓库没有对应构件,则遍历所有远程仓库,发现后解析下载;
(4)如果依赖的版本为RELEASE或LATEST,则读取所有远程仓库的元数据groupId/artifactId/version/maven-metadata.xml,与本地元数据合并后,计算出RELEASE或LATEST的真实值,然后基于真实值检查本地仓库和远程仓库,如步骤(2)(3);
(5)如果依赖的版本为SNAPSHOT,类似的,读取远程仓库的元数据,并与本地元数据合并,计算出最新版本的快照,再从本地仓库和远程仓库检索。
下边为一个快照版本依赖的元数据maven-metadata-local.xml,包括最近更新时间戳,以及存在的不同版本:
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<groupId>com.test</groupId>
<artifactId>C</artifactId>
<versioning>
<versions>
<version>1.0-SNAPSHOT</version>
<version>2.0-SNAPSHOT</version>
</versions>
<lastUpdated>20171113125841</lastUpdated>
</versioning>
</metadata>
对应文件目录结构如下:

其中每个版本中,包含对自身描述的元数据。以2.0-SNAPSHOT为例,其目录结构如下,主要包含jar和pom两部分,maven-metadata-local.xml为依赖元数据:

具体元数据内容如下,记录了该版本依赖最近一次的更新时间。在本地库中没有该构件的时候,会检索所有远程仓库,结合元数据文件,计算最新的版本;或者在进行强制更新的时候,也会计算出最新版本。
<?xml version="1.0" encoding="UTF-8"?>
<metadata modelVersion="1.1.0">
<groupId>com.test</groupId>
<artifactId>C</artifactId>
<version>2.0-SNAPSHOT</version>
<versioning>
<snapshot>
<localCopy>true</localCopy>
</snapshot>
<lastUpdated>20171117132322</lastUpdated>
<snapshotVersions>
<snapshotVersion>
<extension>jar</extension>
<value>2.0-SNAPSHOT</value>
<updated>20171117132322</updated>
</snapshotVersion>
<snapshotVersion>
<extension>pom</extension>
<value>2.0-SNAPSHOT</value>
<updated>20171117132322</updated>
</snapshotVersion>
</snapshotVersions>
</versioning>
</metadata>
3. 依赖传递性解析实例
创建3个Maven工程A、B、C(命令mvn archetype:generate)。
(1)编辑各自的POM文件,使其依赖关系如下图所示:

其中B为A的直接依赖,C具有传递性,为A的传递性依赖,分析其依赖树可以直观的看出依赖关系(mvn dependency:tree),如下:
- B依赖于C(1.0)

- A依赖于B(1.0),间接依赖C(1.0)

(2)将B对的C的依赖改为optional,即可选的,此时A的依赖树如下,不包括C:

(3)修改POM文件,使其依赖关系如下:

看A的依赖树,如下:

综上,正常情况下依赖是具有传递性,除非声明为optional。
总结:
- 声明依赖时,除了常用的三项位置元素,还具有包括范围、类型、可选和排除等;
- 依赖具有传递性,具有路径优先的约定;
- 当引用多个工程时,会潜在的引用其他依赖,需要注意是否会引错包,或者冲突;
- Maven依赖解析,对于本地库中没有的构件,Maven会综合远程仓库与本地仓库的元数据,计算最新版本后,再引用。
参考:
- 《Maven实践》
Maven依赖解析的更多相关文章
- Maven 依赖树的解析规则
对于 Java 开发工程师来说,Maven 是依赖管理和代码构建的标准.遵循「约定大于配置」理念.Maven 是 Java 开发工程师日常使用的工具,本篇文章简要介绍一下 Maven 的依赖树解析. ...
- Maven 依赖调解源码解析(一):开篇
本文是系列文章<Maven 源码解析:依赖调解是如何实现的?>第一篇,主要做个开头介绍.并为后续的实验做一些准备.系列文章总目录参见:https://www.cnblogs.com/xia ...
- Maven 依赖调解源码解析(六):dependencyManagement 版本锁定
本文是系列文章<Maven 源码解析:依赖调解是如何实现的?>第六篇,主要介绍 dependencyManagement 版本锁定原则.请按顺序阅读其他系列文章,系列文章总目录参见:htt ...
- Maven 依赖调解源码解析(七):总结
本文是系列文章<Maven 源码解析:依赖调解是如何实现的?>第七篇,也是最后一篇,主要做个总结.请按顺序阅读其他系列文章,系列文章总目录参见:hhttps://www.cnblogs.c ...
- (十)Maven依赖详解
1.何为依赖? 比如你是个男的,你要生孩子,呸呸呸...男的怎么生孩子,所以你得依赖你老婆,不过也不一定咯,你也可以依赖其她妹子. 我们在平时的项目开发中也是同理,你需要依赖一些东西才能实现相应的功能 ...
- Maven依赖(转)
相同依赖级别,先加入的先依赖不同依赖级别,级别短的先依赖 version-->SNAPSHOTxxx-里程碑-->SNAPSHOT,alpha,beta,Release(RC),GA()s ...
- Maven依赖分析
背景 昨天帮一位同事排查了一个依赖冲突的问题.问题的现象就是在IntelliJ IDEA运行项目正常,但是打包(Maven assembly jar)之后传到服务器运行失败,报错:Caused by: ...
- 转:MAVEN依赖的是本地工程还是仓库JAR包?
相信大家都碰见过 maven 配置的依赖或者是 jar 包或者是工程,在开发的过程当中,我们当然需要引入的是工程,这样查看 maven 依赖的文件的时候,就能直接查看到源码. 一.本地工程依赖 举个例 ...
- Maven依赖的是本地工程还是仓库jar包?
相信大家都碰见过maven配置的依赖或者是jar包或者是工程,在开发的过程当中,我们当然需要引入的是工程,这样查看maven依赖的文件的时候,就能直接查看到源码. 一.本地工程依赖 举个例子,其架构如 ...
随机推荐
- zoj 1874 水题,输出格式大坑
Primary Arithmetic Time Limit: 2 Seconds Memory Limit: 65536 KB Children are taught to add mult ...
- ORALCE PL/SQL学习笔记
ORALCE PL/SQL学习笔记 详情见自己电脑的备份数据资料
- bind、apply与call
bind.apply与call 先说观点:不论是bind.apply还是call,最大的好处就是代码复用. bind 在开发中,我们只有复用代码时,才会出现this指向需要改动的情况. 纵观bind的 ...
- XtraReports 入门教程
一个链接:http://www.cnblogs.com/springSky/tag/XtraReports%20%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B/ 与之相同功能 ...
- PHP 页面静态化/纯静态化/伪静态化
个人博客迁移至独立博客:https://blog.plcent.com/,欢迎大家访问 概念 PHP静态化分为:纯静态化 和 伪静态化:纯静态化又分为:局部静态化 和 完全静态化 纯静态化:是把PHP ...
- Linux 快捷键汇总(偏基础)
自己最近才搭上Linux末班车,有一种想见恨晚的感觉,完全给你一种快速清爽的感觉! 因为需要,所以学习,记录自己在使用Linux系统上的点滴,偏基础! 1. 打开终端: Ctrl+Alt+T 2. 复 ...
- Python3中urllib详细使用方法(header,代理,超时,认证,异常处理) 转
urllib是python的一个获取url(Uniform Resource Locators,统一资源定址器)了,我们可以利用它来抓取远程的数据进行保存哦,下面整理了一些关于urllib使用中的一些 ...
- 一张表搞懂各种 Docker 监控方案 - 每天5分钟玩转 Docker 容器技术(86)
前面我们已经介绍了ps/top/stats.Sysdig.Weave Scope.cAdvisor 和 Prometheus 多种容器监控工具和方案,是时候做一个比较了.下面将从五个方面来对比它们之间 ...
- 快速配置vs2012+opencv
关于OpenCV+Windows+VS配置的文章网上有很多,多是类似 OpenCV中文网 上的安装方法. 不管什么方法,配置的步骤毫无疑问是: 1. 配置环境变量, 2. 配置VS. 在这个过程中,令 ...
- Ionic3 启动页以及应用图标
将新的启动页和应用图标图片(最好是高清png)上传到根目录 resources 使用命令自动生成,通过CMD进入项目所在文件夹,分别执行 ionic cordova resources android ...