maven的一个依赖声明可以包含以下一些基本元素

<project>
     ...
     <dependencies>
         <dependency>
             <groupId>...</groupId>
             <artifactId>...</artifactId>
             <version>...</version>
             <type>...</type>
             <scope>...</scope>
             <optional>...</optional>
             <exclusions>
                 <exclusion>
                     ...
                 </exclusion>
                 ...
             </exclusions>
         </dependency>
         ...
     </dependencies>
     ...
 </project>

根元素project下的dependencies可以包含一个或者多个dependency元素,以声明一个或者多个项目依赖。每个依赖可以包含的元素有:

groupId、artifactId和version:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,Maven根据坐标才能找到相应的依赖。

type:依赖的类型,对应于项目坐标定义的packaging。大部分情况下,该元素不必声明,其默认这为jar。

scope:依赖的范围

optional:标记依赖是否可选

假设有这样一个依赖关系,项目A依赖于项目B,项目B依赖于项目X和Y,B对于X和Y的依赖都是可选的。A -> B、B -> X(可选)、B -> Y(可选),根据传递性依赖的定义,如果所有这三个依赖的范围都是compile,那么X、Y就是A的compile范围传递性依赖。然而,由于X、Y都是可选依赖,依赖不会得以传递。换句话说,X、Y将不会对A有任何影响。

为毛要使用可选依赖这一特性呢,可能项目B实现了两个特性,其中特性一依赖于X,特性二依赖Y,而且这两个特性是互斥的,用户不可能同时使用这两个特性。比如B是一个持久层隔离工具包,它支持多种数据库,包括MYSQL,PostgreSQL等,在构建工具包的时候,需要这两种数据库的驱动程序,但在使用这个工具包的时候,只会依赖一种数据库。

maven忽略间接依赖 maven 节点配置依赖_maven忽略间接依赖

上述XML代码片段中,使用<optional>元素表示mysql-connector-java和postgresql这两个依赖为可选依赖,它们只会对当前项目B有影响,当其他项目依赖于B的时候,这两个依赖不会被传递。因此,当项目A依赖于项目B的时候,如果其实际使用基于MYSQL数据库,那么项目A就需要显式的声明mysql-connector-java这一依赖。

maven忽略间接依赖 maven 节点配置依赖_exclusion_02

exclusions:用来排除传递性依赖

传递性依赖会给项目隐式的引入很多的依赖,这极大的简化了项目依赖的管理,但是有些时候这种特性也会带来问题。例如,当前项目有一个第三方依赖,而这个第三方依赖由于某些原因依赖了另外一个类库的SNAPSHOT版本,那么这个SNAPSHOT就会成为当前项目的传递性依赖,而SNAPSHOT的不稳定性会直接影响到当前项目。这时就需要排除掉该SNAPSHOT,并且在当前项目中声明该类库的某个正式发布版本。还有一些情况,可能想要替换某个传递性依赖,比如Sun JTA API,Hibernate依赖于这个jar,但是由于版权的因素,该类库不在中央仓库中,而Apache Geronimo项目有一个对应的实现。这时就可以排除Sun JTA API,再声明Geronimo的JTA API实现。

maven忽略间接依赖 maven 节点配置依赖_exclusion_03

上述配置中,项目A依赖于项目B,但是由于一些原因,不想引入传递性依赖C,而是自己显示的声明对项目C1.1.0版本的依赖。

需要注意的是,声明exclusion的时候,只需要groupId和artifactId,而不需要version,这是因为只需要groupId和artifactId就能唯一定位依赖图中的某个依赖。换句话说,Maven解析后的依赖中,不可能出现groupId和artifactId相同,但是version不同的两个依赖。

大部分依赖声明只包含基本坐标,然而在一些特殊的情况下,其他元素至关重要。


Maven有以下几种依赖范围:

compile:

编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效。典型的例子是spring-core,在编译、测试和运行的时候都需要使用该依赖。

test:

测试依赖范围。使用此依赖范围的Maven依赖,只对测试classpath有效,在编译主代码或者运行项目的使用时将无法使用此类依赖。典型的例子是JUnit,它只有在编译测试代码及运行测试的时候才需要。

provided:

已提供依赖范围。使用此依赖范围的Maven依赖,对于编译和测试classpath有效,但在运行时无效。典型的例子是servlet-api,编译和测试项目的时候需要该依赖,但在运行项目的时候,由于容器已经提供,就不需要Maven重复的引入一遍。

runtime:

运行时依赖范围。使用此依赖范围的Maven依赖,对于测试和运行classpath有效,但在编译主代码时无效。典型的例子是JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动。

system:

系统依赖范围。该依赖与三种classpath(编译classpath、测试classpath、运行classpath)的关系,和provided依赖范围完全一致。但是,使用system范围的依赖必须通过systemPath元素显示的指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此应该谨慎使用。systemPath元素可以引用环境变量如:

<dependency>
     <groupId>javax.sql</groupId>
     <artifactId>jdbc-stdext</artifactId>
     <version>2.0</version>
     <scope>system</scope>
     <systemPath>${java.home}/lib/rt.jar</systemPath>
 </dependency>

import:

导入依赖范围。该依赖范围不会对三种classpath产生实际的影响。

依赖范围与classpath的关系

依赖范围(Scope)

对于编译classpath有效

对于测试classpath有效

对于运行时classpath有效

例子

compile

Y

Y

Y

spring-core

test

-

Y

-

JUnit

provided

Y

Y

-

servlet-api

runtime

-

Y

Y

JDBC驱动实现

system

Y

Y

-

本地的,Maven仓库之外的类库文件