一个简单的pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache</groupId>
<artifactId>hello-world</artifactId>
<version>1.0-SNAPSHOT</version>
<name> MAVEN Hello World Project</name>
</project>
代码的第一行是XML头,指定了xml文档的版本和编码格式。project元素是所有pom.xml的根元素,它声明了一些POM相关的命名空间及xsd元素,虽然这些属性不是必须的,但使用这些属性可以让第三方工具(如IDE中的XML编辑器)帮助我们快速的编辑pom.根元素下的第一个元素modelVersion指定了当前POM的版本,对于Maven 2 及 Maven 3来说,它只能是4.0.0。name元素声明了一个对于用户更友好的项目名称,这不是必须的,声明名称,便于交流。
maven坐标
<groupId>org.sonatype.nexus</groupId>
<artifactId>nexus-indexer</artifactId>
<version>2.0.0</version>
<package>jar</package>
groupId:定义当前Maven项目隶属的实际项目。
artifactId:定义实际项目中的一个Maven项目(模块),推荐的做法是使用实际项目名称作为artifactId的前缀。
version:定义Maven项目当前所处的版本
packaging:定义Maven项目的打包方式。
classifier:用来帮助定义构建输出的一些附属构建。附属构件与主构件对应,如上例中的主构件是nexus-indexer.2.0.0.jar,这项目可能还会通过使用一些插件生成nexus-indexer.2.0.0-javadoc.jar、nexus-indexer.2.0.0-sources.jar这样一些附属构件,其包括了Java文档和源代码。这时候,javadoc和sources就是这两个附属构件的classifier。不能直接定义classifier,因为附属构件不是项目直接默认生成的,而是由附加的插件帮助生成。
同时构件的名称是与坐标相对应的,一般的规则为artifactId-version[-classifier].packaging
依赖的配置
<dependency>
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>2.0.0</version>
<type>...</type>
<scope>...</scope>
<optional>...</optional>
<exclusions>
<exclusion>
...
</exclusion>
</exclusions>
<dependency>
- groupId、artifactId和version:依赖的基本坐标
- type:依赖的类型,对应于项目坐标定义的packaging
scope:依赖的范围。Maven在编译主代码的时候需要使用一套classpath,在编译和执行测试的时候会使用另外一套classpath,在实际运行Maven项目的时候又会使用一套classpath,依赖的范围就是用来控制依赖与这三种classpath(编译classpath、测试classpath、运行classpath)的关系,Maven有以下几种依赖范围:
- compile:编译依赖范围。没有指定,就会默认使用该依赖范围,使用此依赖范围的Maven依赖,对编译、测试、运行三种classpath都有效;
- test:测试依赖范围。使用此依赖范围的Maven依赖,只对测试classpath有效。
- provided:已提供依赖范围。使用此依赖范围的Maven依赖,对于编译和测试classpath有效。典型的例子是servlet-api,编译和测试的时候需要该依赖,但在运行的时候,由于容器已经提供,就不需要Maven重复的引入一遍。
- runtime:运行时依赖。使用此依赖范围的Maven依赖,对于测试和运行classpath有效。典型的例子是JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC驱动接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动。
system:系统依赖范围。该依赖范围和三种classpath的关系,和provided依赖范围完全一致。但是,使用system范围的时候必须通过systemPath元素显式的指定依赖文件的路径,由于此依赖不是Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此应谨慎使用。
<dependency> <groupId>...</groupId> <artifactId>...</artifactId> <version>...</version> <scope>system</scope> <systemPath>${JAVA_HOME}/lib/rt.jar</systemPath> </dependency>
import : 导入依赖范围。该依赖范围不会对三种classpath产生实际的影响。
- optional:标记依赖是否可选。
- 使用场景:可能B项目实现了两个特性,其中的特性一依赖与X,特性二依赖与Y,而这两个特性是互斥的,用户不可能同时使用两个特性。比如B是一个持久层隔离工具包,它支持多种数据库,包括MySQL、PostgreSQL等,在构建这个工具包的时候,需要这两种数据库的驱动程序,但在使用这个工具包的时候,只会依赖一种数据库。
- exclusions:用来排除传递性依赖。
依赖传递
定义:Maven项目A依赖compile范围的B,B依赖compile范围的C,则C会成为A的compile范围依赖
传递性依赖和依赖范围:
如果A依赖B,B依赖C,我们说A对于B是第一直接依赖,B对于C是第二直接依赖,A对于C是传递性依赖。第一直接依赖的范围和第二直接依赖的范围决定了传递性依赖的范围。最左边一列表示第一直接依赖范围,最上面一行表示第二直接依赖范围,中间的交叉单元格表示传递性的依赖范围。
comile | test | provided | runtime | |
---|---|---|---|---|
compile | test | —— | —— | runtime |
test | test | —— | —— | test |
provided | provided | —— | provided | provided |
runtime | runtime | —— | —— | runtime |
依赖调解
如果,项目A有这样的依赖关系:A->B->C->X(1.0)、A->D->X(2.0),X是A的传递性依赖,但是两条依赖路径上有两个版本的X,只能有一个版本被解析,解析规则如下:
第一原则:路径近者优先。该例中X(1.0)的路径是3,而X(2.0)的路径是2,因此X(2.0)会被解析使用。
第二原则:第一声明者优先(从2.0.9开始)。在依赖路径长度相等的前提下,在POM中依赖的声明顺序决定了谁会被解析使用,顺序最靠前的那个依赖优胜。