坐标

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,规则如下:

  1. 首先以依赖的深度来选择,浅的优先,所以C(3.0)不可能被加载
  2. 在深度相同的情况下,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中需要根据实际使用的数据库,声明使用的连接包