坐标
Maven的坐标用来唯一标示一个项目在Maven仓库的位置,Maven的坐标主要由groupId、artifactId、version、packaging、classifier 5个元素组成,其中groupId、artifactId、version是必要的元素,而packaging、classifier可以省略。
- groupId:定义当前项目所属的项目包,对应了Maven仓库中的目录结构
- artifactId:一般为当前项目名称,对应了项目名称目录名
- version:项目的版本,对应了版本目录名
- packaging:指定打包的方式,默认为jar包,可选(war、maven-plugin、pom、zip等)
- classifier:用于帮助定义构建输出的一些附属构建,不能直接定义通常有插件帮助生成
当使用坐标去maven仓库中找寻依赖文件时,将会使用以下路径查找:
$RESPORITY_HOME/groupId(replace . to /)/artifactId/version/artifactId-version[-classifier].packaging (当packaging为maven-plugin时扩展名也为jar)
当version为SNAPSHOT(快照)版本,发布到远程仓库,SNAPSHOT会被替换成当时的时间戳,当在被依赖时,会时间最近时间戳的版本
依赖
Maven的pom文件中配置依赖大致包含下面的元素:
<dependency>
<groupId></groupId>
<artifactId></artifactId>
<version></version>
<type></type>
<scope></scope>
<optional></optional>
<exclusions>
<exclusion></exclusion>
</exclusions>
</dependency>
<dependency>
<groupId></groupId>
<artifactId></artifactId>
<version></version>
<type></type>
<scope></scope>
<optional></optional>
<exclusions>
<exclusion></exclusion>
</exclusions>
</dependency>
- groupId、artifactId、version用来唯一的表示一个构建
- type:依赖的类型,对应了项目坐标定义时的packaging,默认为jar
- scope:依赖的范围,可选值有compile、test、provided、runtime、system、import
- optional:标记依赖是否为可选依赖,默认为false
- exclusions:用来排除传递性依赖
依赖的范围
- compile:编译范围依赖,默认选项,对于编译、测试、运行三个阶段都需要依赖
- test:测试范围依赖,只在测试阶段需要用的依赖,如junit
- provided:已提供范围依赖,对于编译和测试阶段需要此依赖,当运行期不需要,常见的如servlet-api,运行期容器提供
- runtime:运行时范围依赖,对于运行和测试阶段需要此依赖,如JDBC驱动的实现,代码在编译时只需要JDK提供的JDBC接口
- system:系统范围依赖,与provided依赖方位一样,只是使用system必须显示的使用systemPath指定依赖文件路径
- import:这个依赖范围比较特殊,并不会影响编译、测试、运行三个阶段,是用来复用某个pom文件中配置的dependencyManagement下的配置内容的,所以使用这个scope的dependency必须是满足一些条件才有效:
(1)必须在dependencyManagement元素内
(2)packging必须为pom
依赖的传递
当前项目的直接依赖可能也需要依赖其他的jar,这种依赖叫做二级依赖,Maven会使用依赖的传递机制帮我们处理好二级及以上的依赖,比如如下依赖:
project -> spring-core -> commons-logging
当前项目依赖于spring-core,而spring-core又依赖于commons-logging,我们并不需要在project的pom文件中显示的申明commons-logging的依赖,Maven会通过spring-core的pom文件最终将commons-logging的依赖加载到project项目中,但是在依赖的传递过程中也会有一定的范围规则,如下图:
一级依赖\二级依赖 | compile | test | provided | runtime |
compile | compile | —— | —— | runtime |
test | test | —— | —— | test |
provided | provided | —— | provided | provided |
runtime | runtime | —— | —— | runtime |
简而言之就是
- 当二级依赖的范围是compile时,传递依赖后的方位和一级依赖相同;
- 当二级依赖的范围是test时,此依赖不会传递;
- 当二级依赖的范围是provided时,只有一级依赖也为provided才会传递;
- 当二级依赖的范围是runtime时,除了一级依赖为compile,会传递runtime范围,其余的范围与一级依赖一致
依赖的调解
当项目的间接依赖存在相同的项目不同的版本时,Maven不允许相同的项目被加载多次,这时候就需要对依赖进行调解,比如:
A->B->C(1.0) ,A->D->C(2.0),A->E->F->C(3.0)
A依赖于B和D和E,但B依赖于C的1.0版本,而D依赖于C的2.0版本,F依赖于C的3.0版本,不可能A中加载三个版本的C,这样做是显然是不对的,所以Maven在遇到这种情况是制定了一个规则来确定到底加载哪个版本的C,规则如下:
- 首先以依赖的深度来选择,浅的优先,所以C(3.0)不可能被加载
- 在深度相同的情况下,pom文件中先被解析到的优先
可选依赖
上面有提到pom文件中声明依赖时可以使用<optional>true</optional>声明该依赖为可选依赖,可选依赖不会被传递,如:
A->B,B->C(可选),B->D(可选)
想上面的C和D都不会被传递,需要在A的pom中明确的声明是使用C还是D,举个形象的例子,B是一个持久化隔离的工具包,它支持多种数据库,C、D可以看作Mysql的连接包、Oracle的连接包,C、D在B的pom中的依赖声明就是可选的,他们不会被传递给A,在A中需要根据实际使用的数据库,声明使用的连接包