在maven的世界里,每一个构件都是由maven坐标唯一标识的,通过这个坐标就可以找到该构件(artifact),当构建项目的时候,例如打包或者install的时候,就会根据该坐标找到该artifact并且下载该artifact。
maven坐标类似于平面几何(x,y),或者立体几何中的(x,y,z),唯一标识一个artifact(构件),可以是jar包,或者是war文件,通常是jar。Maven坐标包括的元素包括groupId,artifactId,version,packing,classifier几个元素,packing默认是jar,当然在聚合项目中,必须是pom,为了复用一些信息,而建造的“父项目”的packing也为pom。Classifier元素可选的,定义了构件输出的一些附属属性,例如主构件是nexus-indexer-2.0.0.jar,该项目可能还通过一些其他插件生成其他附属构件,例如javadoc文档、sources源代码,nexus-indexer-2.0.0-javadoc.jar、nexus-indexer-2.0.0-sources.jar,这里的javadoc和sources就是classifier。groupId,artifactId,version是必须定义的,packing是可选的,默认为jar,classifier是不能直接定义的。同时项目构件的文件名是与坐标相对应的,一般的规则为artifactId-version-[classifier].packing。
典型的maven依赖如下:
<project>
…………
<dependencies>
<dependency>
<groupId>…</groupId>
<artifactId>…</artifactId>
<version>…</version>
<type>…</type>
<scope>…</scope>
<option>…</option>
<exclusions>
<exclusion>
……
</exclusion>
</exclusions>
<dependency>
……
<dependencies>
…………
</project>
groupid、artifactid和version已经在之前的博文介绍过了,下面介绍下下面的几个
type:依赖的类型,对应于项目坐标定义的packing。通常情况下,该元素不必声明,默认情况下为jar
scope:依赖范围。默认为compile
option:标记依赖是否可选
exclusions:用于排除传递性依赖。排除依赖一般应用于传递依赖的排除。假设A依赖B,B又依赖于C,但是这个C是非稳定版本。那么就排除该不稳定的C的依赖。然后直接依赖一个稳定版本的C。
依赖范围:控制依赖与三种classpath(编译、测试、运行)的关系
1、complie范围:对于编译,测试,运行三种classpath均有效,例如spring-core.jar
2、Test范围:只对测试classpath有效,例如junit
3、provided范围:又名已提供依赖范围,对于编译和测试classpath有效,最典型的是 servelt-api,在编译和测试的时候需要提供它,但是运行时,一般容器已经提供了servelt-api,所以在运行时classpath不需要此jar。
4、runtime范围:运行时依赖,对于测试和运行classpath有效,最典型的是JDBC驱动实现,在编译的时候,只需要JDBC驱动提供的接口,到了测试和运行的时候,才需要具体实现JDBC接口的驱动。
5、system范围:系统依赖范围,与provided一样,但是使用该依赖范围的依赖必须使用systemPath显示得制指定依赖文件的路径。由于此类依赖不是通过maven仓库解析的,往往与本地系统绑定,造成系统移植性差,所以慎重使用。
6、import依赖:该依赖范围不会对三种classpath产生实质性的影响,主要与dependencyManagement有关。
传递依赖:以las-im-basis项目为例子。
las-im-basis直接以compile范围依赖spring-core,spring-core直接以compile范围依赖commons-logging,所以las-im-basis以compile范围间接依赖commons-logging。
下面的表左边第一列表示第一直接依赖范围,最上面一行表示第二直接依赖范围,中间的交叉表示的是传递依赖范围
下面介绍几个术语
1、依赖调解
A->B->C->X(1.0),A->D->X(2.0),X是A的传递性依赖,但是有两条路径,到底是哪个版本的X会被maven解析呢,maven遵循路径短者优先,所以X(2.0)会被解析。如果这两个路径长度相同呢,则第一声明者优先,也就是依赖在pom的顺序,顺序前者优先。
2、可选依赖
A->B->X(可选),A->B->Y(可选),则X,Y对A的三种classpath不产生什么实际效果,要想X或者Y对A产生作用,需要显示得在A的pom中声明X或者Y的依赖。可选依赖X、Y在B项目中的pom的表示是<option>true</option>,X、Y在B项目中是互斥的,在某一时刻只能有一个在起作用。
Maven的最佳实践(前人的经验总结)
排除依赖:A->B->C,由于B引用的C为非稳定版本,所以需要排除B依赖的C,在A项目中再依赖一个稳定版本的C,排除依赖用<exclusions>元素。
归类依赖:举例来说如果项目有很多的spring framework的依赖,可以遇见的事这些依赖的版本都是一样的,且以后升级的时候,需要一起升级。这个时候就需要归类了
<project>
……
<properties>
<springframework.version>2.5.6</springframework.version>
</properties>
……
<dependencies>
<dependency>
<groupId>…</groupId>
<artifactId>…</artifactId>
<version>${springframework.version}</version>
</dependency>
</dependencies>
…….
</project>
关于优化依赖
优化依赖。Maven会自动解析所有项目的直接依赖和传递性依赖,并且根据规则确定每个依赖的范围,对于一些依赖冲突,也能进行调节,以确定每个构件只有唯一的版本在依赖中存在,经过上述工作得到的依赖,被成为已解析依赖,在命令行中输入:
mvn dependency:list查看已解析依赖;mvn dependency:tree查看已解析依赖构成的依赖树;mvn dependency:analyze分析已解析的依赖,会得到Used undeclared dependencies列表,但是不能简单将在其中的依赖删掉,因为mvn dependency:analyze只能分析编译主代码和测试代码需要用到的依赖,一些执行测试和运行时需要的依赖就没有办法得到了,所以在Used undeclared dependencies中很有可能有在运行时使用的依赖!例如servlet-api。