Maven坐标是通过一些元素定义的,它们分别是groupId、artifactId、version、packing、classifier。Maven仓库连接地址:https://mvnrepository.com/,在这里可有搜索到大部分的maven引用。首先来看一组maven坐标定义,如下:

<dependency>
    <groupId>org.sonatype.nexus</groupId>
    <artifactId>nexus-indexer</artifactId>
    <version>2.0.3</version>
</dependency>

这是nexus-indexer的坐标定义,nexus-indexer是一个对Maven仓库编纂索引并提供搜索功能的类库,它是Nexus项目的一个子模块。在上述代码片段中,其坐标分别为groupId:org.sonatype.nexus、artifactId:nexus-indxer、version:2.0.3、packaging:jar,没有classifier。下面详细解释个坐标元素。

groupId:定义当前Maven项目隶属的实际项目。首先,Maven项目和实际项目不一定是一对一的关系。比如SpringFranework这一实际项目,其对应的Maven项目会有很多,比如spring-core、spring-context等。这是由于Maven中模块的概念,因此一个实现的项目往往会被划分成很多模块。其次,groupId不应该对应项目隶属的组织或公司。原因很简单,一个组织或公司下会有很多的实际项目,如果groupId只定义到组织或公司级别,而后我们将看到artifcatId只能定义Maven项目(模块),那么实际项目这个层将难以定义。最后,groupId的表示方式与Java包名的表示方式类似,通常与域名反向一一对应。在上例子中,groupId为org.sonatype.nexus,org.sonatype表示Sonatype公司建立的一个非盈利性的组织,nexus表示Nexus这一个实际项目,该groupId与域名nexus.sonatype.org对应。

artifactId:该元素定义实际项目中的一个Maven项目(模块),推荐的做法是使用实际项目名称作为artifactId的前缀。比如上例子中的artifactId是nexus-indexer,使用了实际项目名nexus作为前缀,这样做的好处是方便寻找实际构件。在默认情况下,Maven生成的构件,其文件名会以artifactId作为开头,如nexus-indexer-2.0.3.jar,使用实际项目名称作为前缀之后,就会方便从一个lib文件夹中找到某个项目的一组构件。例如,如果有五个项目,每个项目都有一个core模块,如果没有前缀,我们会看到很多的core-1.0.0.jar这样的文件,加上实际项目名作为前缀后,便能很容易区分foo-core-1.0.0.jat、bar-core-1.0.0.jar等。

version:该元素定义Maven项目当前所处的版本,如上例中nexus-indexer的版本是2.0.3。需要注意的是,Maven定义了一套完整的版本规范,以及快照(SANPSHOT)的概念。

packaging:该元素定义Maven项目的打包方式。首先,打包方式通常与所生成构件的文件扩展名对应,如上例中packaging为jar,最终的文件名为nexus-indexer-2.0.3.jar,而使用war打包方式的Maven项目,最终的构件会有一个.war文件,不过这不是绝对的。其次,打包方式会影响到构建的声明周期,比如jar打包和war打包会使用不同的命令。最后,当不定义packaging时,Maven默认值为jar。

classifier:该元素用来帮助定义构建输出的一些附属构件。附属构件与主构件对应,如上例的主构件是nexus-indexer-2.0.3.jar,该项目可能还会通过使用一些插件生成如nexus-indexer-2.0.3-javadoc.jar、nexus-indexer-2.0.3-sources.jar这样一些附属构件,其中包含了Java文档和源代码。这时候,javadoc和sources就是这两个附属构件的classifier。这样附属构件也就拥有了自己唯一的坐标。注意,不能直接定义项目的classifier,因为附属构件不是项目直接默认生成的,而是通过附加的插件生成。

上述5个元素中,groupId、artifactId和version是必须定义的,packaging是可选的(默认为jar),而classifier是不能直接定义的。

同时,项目构件的文件名是与坐标相对应的,一般的规则为artifactId-version [-classifier].packaging,[-classifier]表示可选的。这里需要强调一下,packaging并非与构件扩展名对应,比如packaging为maven-plugin的构件扩展名为jar。

 

上述对Maven的坐标进行了一个简单的概述,这里主要是对Maven的依赖范围。我们知道JUnit依赖的测试范围是test,依赖范围使用scope元素表示。这里需要说明的是Maven在编译项目主代码的时候需要使用一套classpath,其次,Maven在编译和执行测试的时候会使用另一套classpath,Junit测试就是使用该套classpath,最后实际运行Maven项目的时候,又会使用一套classpath。Maven依赖范围就是用来控制依赖与这三种classpath(编译classpath、测试classpath、运行classpath)的关系。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的关系,和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(Maven2.0.9以上版本):导入依赖范围。该依赖范围不会对三种classpath产生实际的影响,主要在dependencyManagement中使用。

下表展示了除了import以外的各种依赖范围与三种classpath的关系:

依赖范围(scope)

对于编译classspath有效

对于测试classpath有效

对于运行时classpath有效

例子

compile

Y

Y

Y

spring-core

test

——

Y

——

JUnit

provided

Y

Y

——

servlet-api

runtime

——

Y

Y

JDBC驱动

system

Y

Y

——

Maven之外的类库文件