1 中央仓库
Maven是从那里下载构件呢,其实maven内置了一个中央仓库的地址(http://repo1.maven.org/maven2),该中央仓库包含了世界上大部分流行的开源项目构件,Maven会在需要的时候去那里下载
2 maven坐标
Maven坐标为各种构件引入了秩序,任何一个构件都必须明确定义自己的坐标,而一组Maven坐标是通过一些元素定义的,它们是groupId、artifactId、version、packing、classifier。下面是一组坐标的定义:
org.sonatype.nexus
nexus-indexer
2.0.0
jar
下面解释一下各个坐标的元素:
groupId:定义当前maven项目隶属的实际项目,例如SpringFramework这一实际项目,其对应的maven项目会有spring-core、spring-context、spring-aop等等,这就是Maven中模块的概念,一个实际的项目往往会划分为许多的模块。
artifactId: 表示实际项目中一个Maven项目模块
version: 表示Maven项目当前所处的版本
packaging: 表示Maven项目的打包方式
classifier: 用来帮助定义构建输出的一些附属构件,例如pac.plugin-2.0.0.jar是主构建,pac.plugin-2.0.0-javadoc.jar和pac.plugin-2.0.0-sources.jar是附属构件。
项目构件的文件名是与坐标相对应的,一般的规则为artifactId-version[-classifier].packing,[-classifier]表示可选。比如nexus-indexer的主构件为nexus-indexer-2.0.0.jar,附属构件有nexus-indexer-2.0.0-javadoc.jar。这里还要强调的一点是,packaging并非一定与构件扩展名对应,比如packaging为maven-plugin的构件扩展名为jar。
3依赖
一个依赖的声明可以包含如下的一些元素:
…
…
…
… 依赖类型,默认为jar
… 依赖范围【compiler、test、provided、runtime、system】
… 标记依赖是否可选
用来排除传递性依赖
……………
……………
3.1 依赖范围
compiler:编译依赖范围。如果没有指定,就会默认使用该依赖范围,对编译、测试、运行三种classpath都有效。
test: 测试依赖范围。只对测试classpath有效
provided: 已提供依赖范围。对编译和测试classpath有效,运行时无效
runtime: 运行时依赖范围。对测试和运行classpath有效,编译主代码时无效
system:系统依赖范围。和provided依赖范围完全一致。但是使用system范围的依赖是必须通过systemPath元素显示地指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往与本机系统绑定,可能造成构建的不可移植,因此应该少用。
import:导入依赖范围,不会对三种classPath产生实际的影响。
下图是依赖范围与classPath的关系:
依赖范围与classPath的关系
3.2 传递性依赖
account-email有一个compiler范围的spring-core依赖,spring-core有一个compiler范围的commons-logging依赖,那么commons-logging就会成为account-email的compiler范围的依赖,commons-logging是account-email的一个传递依赖,如下图所示:
传递性依赖图
从上图中,我们可以看出:当第二直接依赖范围是compiler的时候,传递性依赖的范围与和一直接依赖的范围一致;当第二直接依赖的范围是test的时候,依赖不会得到传递;当第二直接依赖范围是provide的时候,只传递第一直接依赖为provide的依赖,且传递的依赖范围也为provide;当第二直接依赖范围是runtime的时候,传递性依赖的范围与第一直接依赖的范围一致,但compiler例外,此传递性依赖范围为runtime。
短路径和第一声明优先的原则:
项目A有这样的依赖关系:A->B->C->X(1.0)和A->D->X(2.0),X为A的传递性依赖,Maven会选择那个版本的X呢,Maven的调解的第一原则是:短路径最近者优先,X(1.0)的路径长度为3,X(2.0)的路径长度为2,因此会选择X(2.0)。
依赖调解的第二原则是第一声明优先的原则:
比如有这样的一种依赖关系:A->B->Y(1.0),A->C->Y(2.0),两种情况下Y的依赖路径是一样的,均为2,根据第一声明优先的原则,在POM中依赖声明的顺序决定了谁会被解析使用,顺序最靠前的那个依赖优先,在该例中,如果B的依声明在C之前,那么Y(1.0)会被解析。
3.4 可选依赖
假设有这样一个依赖关系,项目A依赖于项目于B,项目B依赖于项目X和Y,B于X和Y的依赖都是可选的:A->B, B->X(可选),B->Y(可选)。根据传递依赖的定义,如果这三个依赖的范围均是compiler,那么X、Y就是A的compiler范围传递性依赖。然而,由于这里X、Y是可选的,依赖不会得以传递。换句话说,X、Y将不会对A有任何影响,如下图所示:
可选依赖
为什么要使用可选择依赖这一特性呢?可能项目B实现了两个特性,其中的特性一依赖于X,特征二依赖于Y,而且这两个特征是互斥的,用户不可能同时使用两个特征。比如B是一个持久层隔离工具包,它支持多种数据库,包括MySQL, PostgreSQL等,在构建这个工具包的时候,需要这两种数据库的驱动程序,但在使用这个工具包的时候,只会依赖一种数据库。项目B的依赖声明见如下代码清单:
4.0.0
com.juvenxu.mvnbook < /groupId>
project-b
1.0.0
mysql
mysql-connector-java
5.1.10
true
postgresql
postgresql
8.4-701.jdb3
true
上述XML片段中,使用3.5 排除依赖
4.0.0
com.juvenxu.mvnbook < /groupId>
project-a
1.0.0
com.juvenxu.mvnbook
project-b
1.0.0
com.juvenxu.mvnbook
project-c
com.juvenxu.mvnbook
project-c
1.1.0
上述代码中,项目A依赖于项目B,但是由于一些原因,不想引入传递性依赖C,而是自己显式声明对于项目C 1.1.0版本的依赖。代码中使用了exclusions元素声明排除依赖。需要注意的是,声明exclusion的时候只需要groupId和artifactId,而不需要version元素,这是因为groupId和artifactId就能唯一定位依赖图中的某个依赖,换句话说,Maven解析后依赖中,不可能出现groupId和artifactId相同,但是version不同的两个依赖。
3.6归类依赖
同一个项目中有很多关于Spring Framework的依赖,这些依赖的模块是来自同一个项目不同的模块,所有这些依赖的版本都是相同的,而且是可预见的,如果要升级,一起升级就可以了,用如下的方法可以解决这个问题就这就是归类依赖:
4.0.0
com.juvenxu.mvnbook < /groupId>
project-b
1.0.0
2.5.6 springframework.version>
org.springframework
spring-core
${springframework.version}
org.springframework
spring-beans
${springframework.version}
org.springframework
spring-context
${springframework.version}
org.springframework
spring-context-support /artifactId>
${springframework.version}
3.7 优化依赖
mvn dependency:list 查看当前项目的已解析的依赖