上篇文章记录了Maven依赖的聚合与继承,POM中依赖的声明通过dependency进行定义,并且通过groupId、artifactId及version三项定位Maven库中的唯一依赖。除了这三项外,还有其他属性进行限制,如下:
1 <dependencies> 2 <dependency> 3 <groupId>...</groupId> 4 <artifactId>...</artifactId> 5 <version>...</version> 6 <type>...</type> 7 <scope>...</scope> 8 <optional>...</optional> 9 <exclusions> 10 <exclusion> 11 <groupId>...</groupId> 12 <artifactId>...</artifactId> 13 </exclusion> 14 </exclusions> 15 </dependency> 16 </dependencies>
我们知道,Maven工程约定具有固定的目录结构,以便于Maven各插件对工程处理,如编译(compile)插件,会将src/main/java中的类编译到target/classes目录下,则编译对应的classpath即为target/classes。依赖范围就是控制依赖于编译classpath(target/classes)、测试classpath(target/test-classes)和运行classpath(以Web工程为例,WEB-INF/classes)的关系。具体依赖范围的讲述可参考官网文档,在此仅进行稍微总结:
依赖传递性,举例说明,比如A引用B,B引用C,正常情况下,A也会引用C依赖,即A经过传递间接引用了C(依赖为可选时,需另行处理)。具体不同范围的依赖经过传递后,其依赖范围的变化如下边(从官网扣下来的)
依赖传递的准则:
可选依赖不被传递。假设项目A依赖于B,B依赖于X和Y,并且X和Y声明为可选,则X和Y对A就不具有传递性了。如果A需要依赖于X或Y,则需要直接引用。
假设项目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之前。
依赖解析的基本过程:当本地仓库中没有依赖构件,则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>
对应文件目录结构如下:
创建3个Maven工程A、B、C(命令mvn archetype:generate)。
(1)编辑各自的POM文件,使其依赖关系如下图所示:
其中B为A的直接依赖,C具有传递性,为A的传递性依赖,分析其依赖树可以直观的看出依赖关系(mvn dependency:tree),如下:
(2)将B对的C的依赖改为optional,即可选的,此时A的依赖树如下,不包括C:
(3)修改POM文件,使其依赖关系如下:
看A的依赖树,如下:
综上,正常情况下依赖是具有传递性,除非声明为optional。
Introduction to the Dependency Mechanism