POM 代表“Project Object Model”。它是 Maven 项目的 XML 表示形式,保存在名为 pom.XML 的文件中。一个项目不仅仅是包含代码的文件集合,还包含配置文件,以及所涉及的开发人员和他们扮演的角色、缺陷跟踪系统、组织和许可证、项目所在地的 URL、项目的依赖关系,以及所有其他为赋予代码生命而发挥作用的小部分。事实上,在 Maven 世界中,项目根本不需要包含任何代码,只需要一个 pom.xml
。
下面是 pom.xml 文件中的元素列表。请注意,modelVersion
必须为 4.0.0
。这是当前唯一受支持的 POM 版本,并且始终是必需的。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- The Basics -->
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<packaging>...</packaging>
<dependencies>...</dependencies>
<parent>...</parent>
<dependencyManagement>...</dependencyManagement>
<modules>...</modules>
<properties>...</properties>
<!-- Build Settings -->
<build>...</build>
<reporting>...</reporting>
<!-- More Project Information -->
<name>...</name>
<description>...</description>
<url>...</url>
<inceptionYear>...</inceptionYear>
<licenses>...</licenses>
<organization>...</organization>
<developers>...</developers>
<contributors>...</contributors>
<!-- Environment Settings -->
<issueManagement>...</issueManagement>
<ciManagement>...</ciManagement>
<mailingLists>...</mailingLists>
<scm>...</scm>
<prerequisites>...</prerequisites>
<repositories>...</repositories>
<pluginRepositories>...</pluginRepositories>
<distributionManagement>...</distributionManagement>
<profiles>...</profiles>
</project>
POM 包含关于项目的所有必要信息,以及构建过程中使用的插件配置。它是“who”、“what”和“where”的声明性表示,而构建生命周期是“when”和“how”。这并不是说 POM 不能影响生命周期的流程——它可以。例如,通过配置 maven-antrun-plugin
插件,可以将 Apache Ant 任务嵌入 POM 中。然而,这最终是一个声明。虽然 Ant 的 build.xml
精确地告诉 Ant 在运行时要做什么,但 POM 声明了它的配置。如果某些外力导致生命周期跳过 Ant 插件的执行,它不会阻止其他插件的执行。这与 build.xml
文件不同,在 build.xml
文件中,任务几乎总是依赖于它之前执行的行。
Maven 坐标
上面定义的 POM 是 Maven 允许的最小值。Maven 中的坐标元素必须包含 groupId
、artifactId
、version
。如果 groupId
和 version
是从其父级继承的,则不需要显式定义。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.sonatype.nexus</groupId>
<artifactId>nexus-indexer</artifactId>
<version>2.0.0</version>
</project>
-
groupId
:该元素定义当前 Maven 项目所隶属的实际项目,在一个实际项目中通常是独一无二的。首先,Maven 项目和实际项目不一定是一对一的关系。比如 SpringFramework 这一实际项目,其下对应的 Maven 项目会有很多,如 spring-core、spring-context 等。这是由于 Maven 中模块的概念,因此,一个实际项目往往会被划分成很多模块。
groupId
不应该对应 Maven 项目所隶属的组织机构或公司。原因很简单,一个组织机构或公司下会有很多实际项目,如果groupld
只定义到组织机构或公司级别,那么artifactld
只能定义实际项目级别,那么 Maven 项目级别(模块)这个层次将难以定义;其次,
groupId
不一定使用点号表示法,例如 junit 项目。请注意,以点号表示的groupId
不必与项目包含的包结构相对应。然而,这是一个值得遵循的良好实践。最后,
groupld
的表示方式与 Java 包名的表示方式类似,通常与倒序的域名一一对应。上例中,groupld
为 org.sonatype.nexus,org.sonatype 表示 Sonatype 公司建立的一个非盈利性组织,nexus 表示Nexus 这一实际项目,该 groupId 与倒序的 nexus.sonatype.org 域名对应。当存储在仓库中时,groupId
中的点号被特定于操作系统的目录分隔符(如 Unix 中的/
)所取代,该分隔符将成为仓库基准目录下的相对目录结构。例如,org.sonatype.nexus 组位于仓库中的$M2_REPO/org/sonatype/nexus
目录中。 artifactId
:该元素定义实际项目中的一个 Maven 项目(模块),推荐的做法是使用实际项目名作为artifactId
的前缀。比如上例中的artifactId
是nexus-indexer
, 使用了实际项目名 nexus 作为前缀,这样做的好处是方便寻找实际工件。在默认情况下,Maven 生成的工件,其文件名会以artifactId
作为开头,如 nexus-indexer-2. 0.0.jar,使用实际项目名称作为前缀之后,就能方便从一个 lib 文件夹中找到某个项目的一组构件。考虑有 5 个项目,每个项目都有一个core模块,如果没有前缀,我们会看到很多 core-1.2. jar 这样的文件,加上实际项目名前缀之后,便能很容易区分 foo-core-1.2.jar、bar-core-1.2.jar 等。artifactId
完全定义了工件在仓库中的存储位置。上述的项目位于仓库中的$M2_REPO/org/sonatype/nexus/nexus-indexer
目录中。version
:该元素定义 Maven 项目当前所处的版本,如上例中 nexus-indexer 的版本是 2.0.0。 需要注意的是,Maven 定义了一套完成的版本规范,以及快照(SNAPSHOT)的概念。version
还用于将仓库中的某个工件各版本分离开来,例如,上述项目 2.0.0 版本的工件位于仓库中的$M2_REPO/org/sonatype/nexus/nexus-indexer
目录中。
现在我们有了 groupId:artifactId:version
的地址结构,还有一个标准的 packaging
元素用来定义 Maven 项目的打包类型。当未声明任何打包类型时,Maven 假定的默认打包类型为 jar
。该元素当前支持的值有:pom
、jar
、maven-plugin
、ejb
、war
、ear
、rar
。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<packaging>war</packaging>
...
</project>
依赖
POM 的基石是其依赖项列表。大多数项目依赖其他项目来正确地构建和运行。在编译时或执行其他需要需要这些依赖项的目标时,Maven 会下载并链接这些依赖项。作为额外的好处,Maven 会引入这些依赖项的依赖项(即使没有显式声明),可传递依赖项,允许您的列表只关注项目所需的依赖项。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<classifier>jar</classifier>
<type>jar</type>
<scope>test</scope>
<systemPath>test</systemPath>
<optional>true</optional>
</dependency>
...
</dependencies>
...
</project>
-
groupId
、artifactId
、version
:该三个元素用于计算特定项目的 Maven 坐标,并将其当作为该项目的依赖项。由于可传递的依赖项,同一工件可以有多个依赖项声明。由于依赖关系由 Maven 坐标描述,这意味着当前项目只能依赖 Maven 的依赖项来管理工件及其依赖关系。不幸的是,有时无法从 Maven 中央仓库下载项目。例如,一个项目可能依赖于一个拥有封闭源代码许可的 jar,该许可不允许它上传到中央存储库中。有三种方法可以处理这种情况。-
使用
maven-install-plugin
插件的install:install-file
目标来将拥有封闭源代码许可的 jar 安装到本地仓库中。例如:mvn install:install-file -Dfile=D:\JavaDev\myapp.jar -DgroupId=com.myapp -DartifactId=myapp-core -Dversion=1.0.0 -Dpackaging=jar
使用
maven-deploy-plugin
插件的deploy:deploy-file
的目标来该拥有封闭源代码许可的 jar 文件部署到您私有的 Maven 仓库中。这样,所以可以连接到该仓库的开发电脑都可以像使用正常依赖那样使用该依赖了。将依赖项的
scope
子元素值设置为system
并定义一个同级的systemPath
子元素,然后指定systemPath
子元素的值为该拥有封闭源代码许可的 jar 文件的本地路径。但是,不建议这样做。
-
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。 这样,附属构件也就拥有了自己唯一的坐标。考虑这样一个项目,它提供了一个针对 Java 11 的工件,但同时也是一个仍然支持 Java 1.8 的工件。第一个工件可以指定classifier
值为 jdk11,第二个工件可以指定classifier
值为 jdk8,这样用户就可以根据需要选择使用哪一个。注意,不能直接定义项目的classifier
, 因为附属构件不是项目直接默认生成的,而是由附加的插件帮助生成。-
type
:指定所选依赖项的类型,大部分情况下不需要声明该元素,默认为jar
。虽然它通常表示依赖项文件名上的扩展名,但情况并非总是这样:类型可以映射到不同的扩展名和classifier
。类型通常与依赖项所的packaging
元素值相对应,尽管情况并非总是如此。新类型可以由将extensions
设置为 true 的插件来定义,因此,下面的列表这不是一个完整的列表。工件处理程序为工件上的每个依赖类型定义信息(classifier
、extension
、language
)以及如何管理该依赖项(添加到类路径,包含到依赖中)。某些工件处理程序默认在META-INF/plexus/artifact-handlers.xml
中配置:type classifier extension packaging language added to classpath includesDependencies pom
= type = type none jar
= type = type java true
test-jar
tests
jar
jar
java true
maven-plugin
jar
= type java true
ejb
jar
= type java true
ejb-client
client
jar
ejb
java true
war
= type = type java true
ear
= type = type java true
rar
= type = type java true
java-source
sources
jar
= type java javadoc
javadoc
jar
= type java true
-
scope
:此元素指定该依赖项的作用范围,以及如何限制该依赖项的可传递性。Maven 在编译项目主代码时需要使用一套compile
的类路径,Maven 在编译和执行测试的时候会使用一套test
的类路径。实际运行 Maven 项目的时候,又会使用runtime
类路径。有五个可用范围:-
compile
:这是默认的范围,在未指定任何范围时使用。该作用范围的依赖项在compile
、test
、runtime
等所有类路径中都可用。 -
provided
:此范围与compile
范围非常相似,但它表示依赖项仅在compile
和test
类路径上可用,在runtime
类路径不可用。典型的就是 servlet-api 依赖项,如果将该依赖项的scope
设置为provided
,那么,该依赖在本地编译和测试时可以使用该依赖项,但运行时会选择使用 Tomcat 或 Jetty 等 Web 容器中的 servlet-api jar 包。 -
runtime
:此范围表示依赖项在test
、runtime
类路径下可用,但在compile
类路径时不可用。典型的就是 JDBC 驱动实现,项目主代码编译时只需要 JDK 提供的 JDBC 接口,只有在执行测试或者运行项目的才用到实现 JDBC 接口的驱动实现。 -
test
:此范围表示该依赖项仅在test
类路径下可用。比如 Junit、TestNG、spring-boot-starter-test 等用作测试的依赖项仅在编译测试代码和运行测试的时候才需要。 -
system
:此范围与provided
的范围一致。使用该范围的依赖项必须配合systemPath
元素一起使用。
-
systemPath
:该元素值指定本地 jar 文件的路径。仅当scope
元素值为system
时才使用该元素,否则,如果设置了该元素,则会导致构建失败。路径必须是绝对路径,因此建议使用 property 指定特定于计算机的路径,例如${java.home}/lib
。Maven 不会去仓库中检查该依赖项,而是会根据该元素值的路径以确保文件存在。如果不存在,Maven 将使构建失败,并建议您手动下载并安装它。optional
:当此项目本身是依赖项时,将依赖项标记为可选的。例如,假设一个项目 A 依赖于项目 B 来编译运行时可能不使用的部分代码,那么我们可能不需要对所有项目都使用项目 B。所以,如果项目 X 将项目 A 添加为它自己的依赖项,那么,Maven 根本不需要安装项目 B。假如用符号表示,如果if=>
表示一个必需的依赖项,-->
表示可选的依赖项,尽管构建项目 A 时的情况可能为A=>B
,构建项目 X 的情况则为X=>A-->B
。optional
范围可以让其他项目知道,当您使用该依赖时,不一定非得需要该依赖项才能正常工作。
依赖解析规则
Maven 引人的传递性依赖机制,一方面大大简化和方便了依赖声明,另一方面,大部分情况下我们只需要关心项目的直接依赖是什么,而不用考虑这些直接依赖会引入什么传递性依赖。但有时候,当传递性依赖造成问题的时候,我们就需要清楚地知道该传递性依赖是从哪条依赖路径引入的。
例如,项目 A 有这样的依赖关系: A->B->C->X(1.0),A->D->X(2.0)。X 是 A 的传递性依赖,但是两条依赖路径上有两个版本的 X,那么哪个 X 会被 Maven 解析使用呢?两个版本都被解析显然是不对的,因为那会造成依赖重复,因此必须选择-一个。Maven 依赖调解(Dependency Mediation)的第一原则是:路径最近者优先。该例中 X(1.0) 的路径长度为 3,而 X(2.0) 的路径长度为 2,因此 X(2.0) 会被解析使用。
依赖调解第一原则不能解决所有问题,比如这样的依赖关系: A->B->Y(1.0),A->C->Y(2.0), Y(1.0) 和 Y(2.0) 的依赖路径长度是一样的,都为 2。那么到底谁会被解析使用呢?在 Maven 2.0. 8 及之前的版本中,这是不确定的,但是从 Maven2. 0.9 开始,为了尽可能避免构建的不确定性,Maven 定义了依赖调解的第二原则:第一声明者优先。在依赖路径长度相等的前提下,在 POM 中依赖声明的顺序决定了谁会被解析使用,顺序最靠前的那个依赖优胜。该例中,如果 B 的依赖声明在 C 之前,那么 Y(1.0) 就会被解析使用。
可以使用 maven-dependency-plugin 的一些目标来辅助查看项目的依赖关系:
-
mvn dependency:list
命令列出已解析出的显式声明的依赖项,不包括间接依赖项。 -
mvn dependency:tree
命令来列出实际生效的依赖项。 -
mvn dependency:analyze
命令来分析此项目的依赖关系并确定哪些是已显式声明且已使用的依赖项、哪些是未显式声明但已使用的依赖项、哪些已显式声明但未使用的依赖项。
依赖版本规范
依赖项的 version
元素定义了版本需求,用来计算依赖项的版本。软需求(Soft Requirement)可以被依赖关系图中同一工件的不同版本所替代。硬需求(Hard Requirement)要求特定的一个或多个版本,并覆盖软需求。如果不存在满足当前项目所有硬需求的依赖项版本,则构建失败。
版本需求具有以下语法:
-
1.0
:1.0 的软需求。如果依赖关系树中之前没有出现其他版本,则使用 1.0。 -
[1.0]
:1.0 的硬需求。使用 1.0 且仅使用 1.0。 -
(,1.0]
:任何 <=1.0 的版本的硬需求。 -
[1.2,1.3]
:1.2 和 1.3 之间任何版本的硬需求(包含 1.2 和 1.3 版本)。 -
[1.0,2.0)
:1.0 <=x< 2.0,位于 1.0 版本(含)和 2.0 版本(不含)之间的任何版本的硬需求。 -
[1.5,)
:任何大于或等于 1.5 的版本的硬需求。 -
(,1.0],[1.2,)
:任何小于或等于 1.0,大于或等于 1.2,但不等于 1.1 的版本的硬需求。多个需求用逗号分隔。 -
(,1.1),(1.1,)
:除 1.1 以外的任何版本的硬需求;例如,因为 1.1 有一个严重的漏洞。
Maven 选择满足当前项目所有硬需求的最高版本的依赖项。如果没有能够满足所有的硬需求的版本,那么构建就会失败。
版本顺序规范
如果版本字符串在语法上是正确的语义化版本 1.0.0 规范 的版本号,那么在几乎所有情况下,版本比较都遵循该规范中概述的优先规则。这些版本是常见字母数字的 ASCII 字符串,如 2.15.2-alpha。更准确地说,如果要比较的两个版本号与语义版本控制规范中 BNF 语法中的 valid semver
产品相匹配,则这是正确的。Maven 不考虑该规范所暗示的任何语义。
重要提示:这仅适用于语义化版本 1.0.0 规范。Maven 版本顺序算法与语义化版本 2.0.0 标准不兼容。特别是,Maven 对加号并不做特殊处理,也不考虑构建标识符。当版本字符串不遵循语义化版本控制时,需要一组更复杂的规则。Maven 使用点号(.
)和连字符(-
)之间标记、数字与字母的转换处来分割出坐标。点号(.
)和连字符(-
)作为分隔符将被记录,并将对顺序产生影响。数字和字幕之间的转换相当于连字符、连续的分隔符之间的空标记将替换为 0
。这将提供一系列带有 .
或 -
前缀的版本号(数字标记)和版本限定符(非数字标记)。分割和替换示例:1-1.foo-bar1baz-.1
->
1-1.foo-bar-1-baz-0.1
。
然后,从版本末尾开始,对尾部的 null
值(0
, ""
, "final
", "ga
")将被切除。该处理在每个剩余的连字符处从头到尾不断重复。切除示例:
-
1.0.0
->1
-
1.ga
->1
-
1.final
->1
-
1.0
->1
-
1.
->1
-
1-
->1
-
1.0.0-foo.0.0
->1-foo
-
1.0.0-0.0.0
->1
版本顺序是该前缀标记序列的字典顺序(lexicographical order
),较短的一个标记填充了足够多的 null
值,并具有匹配的前缀,与较长的标记具有相同的长度。填充 null
值取决于另一版本的前缀:0
表示 .
,而 -
表示 .
。带前缀的标记顺序为:
-
如果前缀相同,则比较标记:
数字标记具有自然顺序。
-
非数字标记(“限定符”)按字母顺序排列,但以下标记按字母顺序排在第一位:"
alpha
" < "beta
" < "milestone
" < "rc
" = "cr
" < "snapshot
" < "" = "final
" = "ga
" < "sp
"- "
alpha
"、"beta
" 和 "milestone
" 限定符在后面直接跟一个数字时,可以分别缩短为a
、b
和m
。
- "
或者:"
.qualifier
" < "-qualifier
" < "-number
" < ".number
"。
最终结果示例:
- "
1
" < "1.1
"(数字填充) - "
1-snapshot
" < "1
" < "1-sp
"(限定符填充) - "
1-foo2
" < "1-foo10
"(正确地自动“切换”到数字顺序) - "
1.foo
" < "1-foo
" < "1-1
" < "1.1
" - "
1.ga
" = "1-ga
" = "1-0
" = "1.0
" = "1
"(移除末尾的null
值) - "
1-sp
" > "1-ga
" - "
1-sp.1
" > "1-ga.1
" - "
1-sp-1
" < "1-ga-1
" = "1-1
"(移除连字符处的null
值) - "
1-a1
" = "1-alpha-1
"
注意:与某些设计文档中所述相反,对于版本顺序,snapshot 的处理方式与 release 或任何其他限定符的处理方式没有区别。
版本顺序测试
Maven 发行版包括一个比较指定版本之间顺序的工具。有疑问时,请自行运行。您可以这样运行它:
java -jar ${MAVEN_HOME}/lib/maven-artifact-3.3.9.jar [versions...]
示例
$ java -jar ./lib/maven-artifact-3.3.9.jar 1 2 1.1
Display parameters as parsed by Maven (in canonical form) and comparison result:
1. 1 == 1
1 < 2
2. 2 == 2
2 > 1.1
3. 1.1 == 1.1
依赖排除
使用 exclusion
元素可以让 Maven 在包含某个依赖项的时候不包含该依赖项的某些间接依赖项(换句话说,它的可传递依赖项)。exclusions
元素可以包含一个或多个 exclusion
元素,每个元素都包含一个 groupId 和一个 artifactId,表示要排除的依赖项。与 optional
(可能安装和使用,也可能不安装、不使用)不同,排除会主动从依赖关系树中删除对应的依赖项。例如,maven-embedder 间接依赖于 maven-core,我们不希望使用它或它的依赖项,那么我们显式地将 maven-core 的 groupId 和 artifact 放在 exclusion
元素中 。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-embedder</artifactId>
<version>2.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
</exclusion>
</exclusions>
</dependency>
...
</dependencies>
...
</project>
如果某个依赖项的作用域可能有无、当前版本有安全漏洞、或与项目中的其他依赖项冲突,就有必要裁掉某个依赖项的可传递依赖项。使用通配符排除可以轻松排除依赖项的所有可传递依赖项。在以下情况下,您可能正在使用 maven-embedder,并且您希望自己管理所使用的依赖项,因此您可以裁掉所有可传递的依赖项:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-embedder</artifactId>
<version>3.1.0</version>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
...
</dependencies>
...
</project>
继承
Maven 为构建管理带来的一个强大的补充是项目继承的概念。诸如 Ant 的构建系统可以模拟继承,但 Maven 在项目对象模型中已经显式地声明了继承。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.codehaus.mojo</groupId>
<artifactId>my-parent</artifactId>
<version>2.0</version>
<packaging>pom</packaging>
</project>
对于父项目来说,其 pom.xml 中的 packaging
元素的值必须为 pom
类型。packaging
元素值会绑定到一组生命周期阶段的目标。例如,如果打包是 jar,那么打包阶段将执行 jar:jar
目标。父 POM 中的大多数元素都可以由其子 POM 继承,包括:
- groupId
- version
- description
- url
- inceptionYear
- organization
- licenses
- developers
- contributors
- mailingLists
- scm
- issueManagement
- ciManagement
- properties
- dependencyManagement
- dependencies
- repositories
- pluginRepositories
- build
- plugin executions with matching ids
- plugin configuration
- etc.
- reporting
- profiles
未继承的元素包括:
- artifactId
- name
- prerequisites
子 POM 示例如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.codehaus.mojo</groupId>
<artifactId>my-parent</artifactId>
<version>2.0</version>
<relativePath>../my-parent</relativePath>
</parent>
<artifactId>my-project</artifactId>
</project>
请注意: relativePath
元素不是必需的,但可以作为 Maven 的一个标志,在搜索本地仓库和远程仓库之前,先搜索 relativePath
元素值所指定的该项目的父项目的路径。
Super POM
与面向对象编程中对象的继承类似,扩展父 POM 的 POM 从父 POM 继承某些值。此外,正如 Java 对象最终继承自 java.lang.Object 一样,所有项目对象模型都继承自一个基础的 Super POM。下面的代码片段是 Maven 3.5.4 的 Super POM。
<project>
<modelVersion>4.0.0</modelVersion>
<repositories>
<repository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<updatePolicy>never</updatePolicy>
</releases>
</pluginRepository>
</pluginRepositories>
<build>
<directory>${project.basedir}/target</directory>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<finalName>${project.artifactId}-${project.version}</finalName>
<testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory>
<sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
<scriptSourceDirectory>${project.basedir}/src/main/scripts</scriptSourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
</resource>
</resources>
<testResources>
<testResource>
<directory>${project.basedir}/src/test/resources</directory>
</testResource>
</testResources>
<pluginManagement>
<!-- NOTE: These plugins will be removed from future versions of the super POM -->
<!-- They are kept for the moment as they are very unlikely to conflict with lifecycle mappings (MNG-4453) -->
<plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-5</version>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.8</version>
</plugin>
<plugin>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.3</version>
</plugin>
</plugins>
</pluginManagement>
</build>
<reporting>
<outputDirectory>${project.build.directory}/site</outputDirectory>
</reporting>
<profiles>
<!-- NOTE: The release profile will be removed from future versions of the super POM -->
<profile>
<id>release-profile</id>
<activation>
<property>
<name>performRelease</name>
<value>true</value>
</property>
</activation>
<build>
<plugins>
<plugin>
<inherited>true</inherited>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<inherited>true</inherited>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<inherited>true</inherited>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<updateReleaseInfo>true</updateReleaseInfo>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
通过创建一个最小的 pom.xml
并在命令行上执行:mvn help:effective-pom
,可以了解 Super POM 是如何影响项目对象模型的。
依赖管理
除了继承某些顶级元素外,父 POM 中的 <dependencyManagement>
元素可以帮助管理其所有子 POM 的依赖关系信息。父 POM 中 <dependencyManagement>
元素中所声明的依赖项详细信息会被子 POM 所继承,如果子 POM 在其 <dependecies>
元素中声明了相同的依赖项,则可以省略某些依赖项配置,比如 version
、scope
。虽然父 POM 的 <dependencyManagement>
元素中设置了某个依赖项的详细信息,但如果子 POM 中没有使用对应的 groupId
和 artifactId
来显式使用该继承的依赖项,那么该子 POM 则不会使用该依赖项。当然,子 POM 也可以根据自己的实际情况来使用不同的依赖项配置来覆盖所继承的依赖项配置。
比如,如果 my-parent 项目在其 dependencyManagement
元素中定义对 junit:junit:4.12
的依赖关系,那么从这个项目继承的子 POM 只需要提供 groupId=junit
和 artifactId=junit
就可以使用父 POM 中声明的这个依赖项,Maven 将自动填充父 POM 中所设置的 JUnit 依赖项版本。这种方法的好处是显而易见的。可以在一个中心位置配置依赖项的详细信息,然后这些配置将传播到所有子 POM。
聚合
通过 <modules>
元素来声明多个模块的项目称为聚合项目(或多模块项目),聚合项目的 <packaging>
元素值也必须为 pom
。<modules>
元素的子元素<module>
指定某个子模块相对于聚合项目的相对路径。一般将聚合项目的子模块项目放在聚合项目根目录下作为子目录存在,但不是绝对。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.codehaus.mojo</groupId>
<artifactId>my-parent</artifactId>
<version>2.0</version>
<packaging>pom</packaging>
<modules>
<!-- 将聚合项目的子模块项目放在聚合项目根目录下作为子目录存在 -->
<module>my-project</module>
<!-- 聚合项目的子模块项目目录与聚合项目目录同级 -->
<module>../another-project</module>
</modules>
</project>
在列出模块时,即使各子模块之间有依赖关系,也不需要考虑子模块声明顺序。Maven 会对模块进行拓扑排序,这样被依赖模块总是在依赖模块之前构建。但各模块之间不能出现循环依赖,否则,Maven 就会失败。
继承 VS 聚合
继承和聚合通过单个高级 POM 创建一个很好的动态来控制构建。父项目和聚合项目的 <packaging>
元素值都是 pom
,两者除了 POM 以外均不含其他内容。经常会看到一个项目既是父项目又是聚合项目,但继承和聚合不是一个概念。两者不同之处如下:
- 父项目的主要目的是为了消除重复配置。父项目的 pom.xml 中可以不含模块而单单作为一个父项目来被其子项目通过
<parent>
元素来继承,此时,父项目仅仅是父项目,而不是一个聚合项目。父项目不知道哪些子项目继承了它,而子项目知道它继承了哪个父项目。 - 聚合项目的目的是便于管理多个模块。聚合项目的 pom.xml 中可通过包含若干个子模块,从而将这些子模块归为一个组来统一管理这些子模块,比如,统一 clean、compile 所有子模块。而子模块不需要特殊的声明。聚合项目知道自己聚合了哪些模块,但子模块不知道谁聚合了自己以及和谁一起被聚合。
property
Maven 的 property 是值占位符,就像 Ant 中的 property 一样。它们的值可以通过使用符号 ${X}
在 POM 中的任何位置访问,其中 X
是 property。或者它们可以被插件用作默认值,例如:
<project>
...
<properties>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<!-- Following project.-properties are reserved for Maven in will become elements in a future POM definition. -->
<!-- Don't start your own properties properties with project. -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
...
</project>
Property 有五种不同的风格:
-
env.X
:在变量前面加上env.
将返回 shell 的环境变量。例如,${env.PATH}
包含 PATH 环境变量。虽然环境变量本身在 Windows 上不区分大小写,但 property 的查找是区分大小写的。换句话说,虽然 Windows shell 为%PATH%
和%Path%
返回相同的值,但 Maven 区分了${env.PATH}
和${env.Path}
。为了可靠性起见,环境变量的名称被规范化为所有大写。 -
project.x
:POM 中以点号(.
)表示的路径将包含相应元素的值。例如:<project><version>1.0</version></project>
可以通过${project.version}
来访问。 -
settings.x
:settings.xml
中以点号(.
)表示的路径将包含相应元素的值。例如:<settings><offline>false</offline></settings>
可通过${settings.offline}
访问。 - Java 系统属性:通过
java.lang.System.getProperties()
访问的所有属性都可以作为 POM property 使用,例如${java.home}
。 -
x
:在 POM 中的<properties/>
元素内设置。<properties><someVar>value</someVar></properties>
的值可以用作${someVar}
。
Build Setting
构建
根据 POM 4.0.0 XSD,<build>
元素在概念上分为两部分,对应一个 BaseBuild
类型,它包含两个 <build>
元素共有的元素集:<project>
元素下和 <profiles>
元素下的顶级 <build>
元素;还有一个 Build
类型,它包含 BaseBuild
集合以及顶层定义的更多元素。让我们从分析两者之间的共同点开始。注意:这些不同的 <build>
元素可以表示为 project build
和 profile build
。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<!-- "Project Build" contains more elements than just the BaseBuild set -->
<build>...</build>
<profiles>
<profile>
<!-- "Profile Build" contains a subset of "Project Build"s elements -->
<build>...</build>
</profile>
</profiles>
</project>
基础构建元素
BaseBuild
类型就像听起来的那样,POM 中两个 <build>
元素中的定级子元素元素的集合。
<build>
<defaultGoal>install</defaultGoal>
<directory>${basedir}/target</directory>
<finalName>${artifactId}-${version}</finalName>
<filters>
<filter>filters/filter1.properties</filter>
</filters>
...
</build>
-
defaultGoal
:当没有给定目标或阶段时所要执行的默认目标或阶段。如果给定了一个目标,那么应该在命令行中定义它(例如jar:jar
)。如果定义了一个阶段(如install
),则情况也是如此。 -
directory
:构建输出的目标目录。它默认为${basedir}/target
。 -
finalName
:这是最终构建项目时的项目名称(无文件扩展名,例如:my-project-1.0.jar)。它默认为${artifactId}-${version}
。然而,finalName
的术语有点用词不当,因为构建项目的插件完全有权忽略/修改这个名称(但通常不会)。例如,如果maven-jar-plugin
插件被配置为给 jar 一个test
的classifier
,那么上面定义的实际 jar 将被构建为 my-project-1.0-test.jar。 -
filter
:指定一个*.properties
文件,该文件中定义的name=value
对在构建时将用于替换资源中的${name}
字符串。上面的示例在filters/
目录下定义了 filter1.properties 文件。Maven 的默认过滤器目录是${basedir}/src/main/filters/
。
资源
<build>
元素的另一个特性是指定资源在项目中的位置。资源通常不是代码,它们不会编译,而是要捆绑在项目中或用于各种其他用途(如代码生成、用作项目的配置文件)。例如,Plexus 项目需要在 META-INF/plexus
目录中存放一个 configuration.xml
文件(指定容器的组件配置)。尽管我们可以同样轻松地将此文件放在 src/main/resources/META-INF/plexus
中,但我们希望将它自己的目录 src/main/plexus
提供给 Plexus。为了让 JAR 插件正确绑定资源,您可以指定类似以下内容的资源:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<build>
...
<resources>
<!-- 指定模块中所用资源所在的目录 -->
<resource>
<!-- 默认资源目录为src/main/resources -->
<directory>src/main/resources</directory>
<!-- filtering设置为true会将文本类型的资源文件中使用${name}表示的属性值替换为对应属性值(这些属性可以有五种声明方式,
参考关于Property的说明 -->
<filtering>true</filtering>
<!-- 指定需要输出的资源文件(文本文件可以使用属性引用的替换) -->
<!-- 不输出src/main/resources目录下的所有.properties和.xml资源文件 -->
<includes>
<include>**/*.properties</include> <!-- 输出src/main/resources目录下的所有txt资源文件 -->
<include>**/*.xml</include> <!-- 输出src/main/resources目录下的所有xml资源文件 -->
</includes>
<!-- 指定不需要输出资源文件(二进制文件不能使用属性引用的替换,否则可能会损坏二进制文件) -->
<!-- 不输出src/main/resources目录下的所有.bmp和.jpg资源文件 -->
<excludes>
<exclude>**/*.bmp</exclude>
<exclude>**/*.jpg</exclude>
</excludes>
</resource>
<!-- 可以使用多个resource元素来指定多个资源目录 -->
<resource>
<directory>src/main/resources-filtered</directory>
<!-- includes和excludes可以组合使用。如下配置会输出资源目录下的所有txt文件但除了名称中包含test的txt文件以外 -->
<includes>
<include>**/*.txt</include>
</includes>
<excludes>
<exclude>**/*test*.*</exclude>
</excludes>
</resource>
</resources>
<testResources>
<testResource>
<!-- ... -->
</testResource>
</testResources>
...
</build>
</project>
-
<resources>
:是<resource>
元素的列表,每个<resource>
元素用于选择将哪些资源文件复制到构建输出的类路径下,执行复制操作之前还可能对文本类资源文件中的 property 引用进行替换。 -
<targetPath>
:资源经构建之后的存放目录。目标路径默认为基本目录。一般会把META-INF
目标目录会打包到 JAR 包中。 -
<filtering>
:为true
或false
,表示是否要替换资源文件中的某些 property 引用。请注意,如果要替换 property 的引用,不一定非要在<filters>
定义*.properties
文件,资源还可以使用 POM 中默认定义的 property (例如${project.version}
)、 传递到命令行的-D
选项参数(例如,-Dname=value
)或由<properties>
元素显式定义的 property。 -
<directory>
:这个元素的值定义了在哪里可以找到资源,默认为${basedir}/src/main/resources
。 -
<includes>
:一组文件模式,使用*
作为通配符,指定<directory>
元素值所指定目录下的哪些文件和目录要包含在<targetPath>
目录下。 -
<excludes>
:与<includes>
元素结构相同,但指定要排除的文件。如果一个路径同时匹配<includes>
和<excludes>
中的模式,则忽略匹配的包含模式而排除处理。 -
<testResources>
:<testResources>
元素包含<testResource>
元素。它们的定义类似于资源元素,但在测试阶段自然使用。一个区别是项目的默认(超级 POM 定义的)测试资源目录是${basedir}/src/test/resources
。未部署测试资源。
再看下面一个 Spring Boot 项目管理不同运行环境下资源文件的例子:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<build>
...
<!-- 声明了三个运行环境的 profile:开发、测试、UAT、生产。当通过id激活了某个 profile 之后,对应的activeProfile属性就会起作用 -->
<profiles>
<profile>
<id>dev</id>
<properties>
<activeProfile>dev-res</activeProfile>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>test</id>
<properties>
<activeProfile>test-res</activeProfile>
</properties>
</profile>
<profile>
<id>uat</id>
<properties>
<activeProfile>uat-res</activeProfile>
</properties>
</profile>
<profile>
<id>pro</id>
<properties>
<activeProfile>pro-res</activeProfile>
</properties>
</profile>
</profiles>
...
<resources>
<resource>
<!-- 默认资源目录为src/main/resources。建议将不同环境下同名的资源文件分开存放 -->
<!-- 假如src/main/resources存在以下子路径:env/dev-res/、env/test-res/、env/uat-res/、env/pro-res/,
每个子路径下存放了一些对应运行环境的资源文件 -->
<directory>src/main/resources</directory>
<!-- 排除env子目录下的所有资源文件 -->
<excludes>
<exclude>env/**</exclude>
</excludes>
</resource>
<!-- 使用另一个resource元素来包含某个激活的 profile 中 activeProfile 属性值所构成的资源路径 -->
<!-- src/main/resources/env/${activeProfile} 路径下的资源文件将直接复制到构建输出目录的类路径下 -->
<resource>
<directory>src/main/resources/env/${activeProfile}</directory>
<includes>
<include>**</include>
</includes>
</resource>
</resources>
...
</build>
</project>
在上面的例子中,通过激活某个 profile 可以选择将 src/main/resources/env/${activeProfile}
路径下的资源文件复制到构建输出的类路径下,就不用来回复制替换不同运行环境下的资源文件了。如果你使用 Intellig IDEA 进行开发,可以直接在 Maven 插件提供的 Profile 复选框勾选来激活对应的 profile,非常方便。也可以通过命令行参数来激活某个 profile。
插件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<build>
...
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<extensions>false</extensions>
<inherited>true</inherited>
<configuration>
<classifier>test</classifier>
</configuration>
<dependencies>...</dependencies>
<executions>...</executions>
</plugin>
</plugins>
</build>
</project>
除了 groupId:artifactId:version
的标准坐标之外,还有一些用于配置插件或与构建进行交互的元素。
extensions
:true
或false
,是否加载此插件的扩展。它默认为false
。inherited
:true
或false
,该插件配置是否应用于从该 POM 继承的子 POM。默认值为true
。-
configuration
:这是特定于单个插件的。在不深入讨论插件如何工作的机制的情况下,只需说明插件 Mojo 可能期望的任何属性(javamojobean中的getter和setter)都可以在这里指定。在上面的示例中,我们将在maven-jar-plugin
的 Mojo 中设置classifier
为test
。值得注意的是,所有配置元素,无论它们位于 POM 中的何处,都旨在将值传递给另一个底层系统,例如插件。换句话说:POM 语法从未显式声明需要configuration
元素中的值,但插件目标完全有权要求必需提供configuration
元素中的值。如果您的 POM 继承自一个父 POM,它将从父级的
<build>/<plugins>
元素和<pluginManagement>
元素中继承插件配置。默认配置继承
为了说明,请看来自某个父 POM 的以下片段:
<plugin> <groupId>my.group</groupId> <artifactId>my-plugin</artifactId> <configuration> <items> <item>parent-1</item> <item>parent-2</item> </items> <properties> <parentKey>parent</parentKey> </properties> </configuration> </plugin>
下面的 POM 片段所在的项目继承了上面 POM 片段所在的项目:
<plugin> <groupId>my.group</groupId> <artifactId>my-plugin</artifactId> <configuration> <items> <item>child-1</item> </items> <properties> <childKey>child</childKey> </properties> </configuration> </plugin>
默认行为是根据元素名称合并配置元素的内容。如果子 POM 具有父 POM 所没有的元素,或子 POM 具有和父 POM 相同的元素,则子 POM 中的该元素将成为有效值。如果父 POM 具有子 POM 所有没有的元素,则父 POM 中的该元素将成为有效值。这纯粹是对 XML 的操作,不涉及插件本身的代码或配置。只涉及元素,而不涉及它们的值。
将这些规则应用到示例中,子 POM 中实际生效的结果如下:
<plugin> <groupId>my.group</groupId> <artifactId>my-plugin</artifactId> <configuration> <items> <item>child-1</item> </items> <properties> <childKey>child</childKey> <parentKey>parent</parentKey> </properties> </configuration> </plugin>
高级配置集成
通过向
<configuration>
元素的子元素添加属性,可以控制子 POM 如何从父 POM 继承配置。属性为combine.children
和combine.self
。在子 POM 中使用这些属性来控制 Maven 如何将父级的插件配置与子级的显式配置相结合。以下是子配置中的两个属性的示例:
<configuration> <items combine.children="append"> <!-- combine.children="merge" is the default --> <item>child-1</item> </items> <properties combine.self="override"> <!-- combine.self="merge" is the default --> <childKey>child</childKey> </properties> </configuration>
那么,有效配置如下:
<configuration> <items combine.children="append"> <item>parent-1</item> <item>parent-2</item> <item>child-1</item> </items> <properties combine.self="override"> <childKey>child</childKey> </properties> </configuration>
combine.children=“append”
将按顺序连接父元素和子元素中的值。另一方面,combine.self=“override”
完全覆盖对应的父配置。如果尝试对一个元素同时使用combine.self=“override”
和combine.children=“append”
,则override
将占据上风。请注意,这些 attribute 仅应用于声明它们的配置元素,而不会传播到下一级的嵌套子元素。也就是说,如果来自子 POM 的
<item>
元素的内容是一个复杂的结构而不是文本,那么它的子元素仍将遵循默认的合并策略,除非这些子元素使用了combine.*
attribute。combine.*
attribute 从父 POM 继承到子 POM。将这些 attribute 添加到父 POM 时要小心,因为这可能会影响其子 POM 或其孙子 POM。 dependencies
:在 POM 中的许多位置都可以看到<dependencies>
元素,当该元素可以出现在<plugin>
元素中时, 其的结构和功能与在基础构建下时相同。不同的是,<plugin>
元素下的<dependencies>
元素不再作为项目的依赖项,而是作为它们所处插件的依赖项。它的功能是改变插件的依赖项列表,可能是通过排除删除未使用的运行时依赖项,或者改变所需依赖项的版本。-
executions
:一个插件可能有多个目标。每个目标可能有一个单独的配置,甚至可能将插件的目标绑定到一个不同的阶段。<executions>
配置一个插件目标的<execution>
。例如,假设您想要将antrun:run
目标绑定到verify
阶段。我们希望任务回显构建目录,并通过将inherited
设置为false
避免将此配置传递给其子级(假设它是父级)。您将得到如下<execution>
:<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> ... <build> <plugins> <plugin> <artifactId>maven-antrun-plugin</artifactId> <version>1.1</version> <executions> <execution> <id>echodir</id> <goals> <goal>run</goal> </goals> <phase>verify</phase> <inherited>false</inherited> <configuration> <tasks> <echo>Build Dir: ${project.build.directory}</echo> </tasks> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
-
id
:不言自明,用于在所有执行块中标识该执行块。当阶段运行时,它将以以下形式显示:[plugin:goal execution: id]
。在本例中:[antrun:run execution: echodir]
。 -
goals
:与所有复数名称的 POM 元素一样,它是一个<goal>
元素的列表。 -
phase
:这是<goals>
列表执行时所处的阶段。这是一个非常强大的选项,允许将任何目标绑定到构建生命周期的任何阶段,从而改变 Maven 的默认行为。 -
inherited
:与上面的<inherited>
元素一样,将此设置为false
将禁止 Maven 将此执行传递给其子级。此元素仅对父 POM 有意义。 -
configuration
:与上面相同,但是将配置限制在这个特定的目标列表中,而不是插件下的所有目标。
-
插件管理
<pluginManagement>
是一个与 <plugins>
同级的元素,用于管理插件。<pluginManagement>
元素包含插件元素的方式与 <plugins>
元素基本相同,不同之处在于,这里的定义仅仅是为子项目或当前项目的 <plugins>
元素中实际引用的插件提供一些默认配置,如果子项目或当前项目的 <plugins>
元素中没有使用 <pluginManagement>
元素中的某些插件声明,则子项目或当前项目就不会使用对应插件。子项目完全可以覆盖所继承的插件管理的定义。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<build>
...
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>pre-process-classes</id>
<phase>compile</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<classifier>pre-process</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
...
</build>
</project>
如果某个子 POM 通过以下片段继承了上面的 POM,或者当前 POM 在包含上面 <pluginManagement>
元素声明的同时,还声明了在当前 POM 中声明了以下片段的配置,则就可以完全按照 <pluginManagement>
元素所声明的那样来使用对应的插件,而不需要完全照抄一份,因为已经从 <pluginManagement>
元素继承了一些默认插件配置。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<build>
...
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
</plugins>
...
</build>
</project>
<build>
中的其他元素
XSD 中的 Build
类型表示那些仅可用于项目构建的元素。尽管有很多额外的元素,但 <project>/<build>
中实际上只包含两组 <profile>/<build>
中所缺少的元素:目录和扩展。
目录
目录元素集位于父 <build>
元素中,父 <build>
元素将 POM 作为一个整体设置各种目录结构。因为它们不存在于 <profile>/<build>
中,所以 <profile>
不能修改它们。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<build>
<sourceDirectory>${basedir}/src/main/java</sourceDirectory>
<scriptSourceDirectory>${basedir}/src/main/scripts</scriptSourceDirectory>
<testSourceDirectory>${basedir}/src/test/java</testSourceDirectory>
<outputDirectory>${basedir}/target/classes</outputDirectory>
<testOutputDirectory>${basedir}/target/test-classes</testOutputDirectory>
...
</build>
</project>
如果上面的 <*Directory>
元素的值设置为绝对路径(当其 property 替换成实际值时),则使用该绝对路径。否则,则使用相对于 ${basedir}
目录的相对路径。请注意,scriptSourceDirectory
在 Maven 中没有使用,已经过时。
扩展
扩展(通过 <extensions>
元素)是要在此构建中使用的工件的列表,这些工件将包含在正在运行的构建的类路径中,这些工件可以支持对构建过程的扩展,以及激活插件从而更改构建生命周期。简而言之,扩展是在构建期间激活的工件。扩展不必实际执行任何操作,也不必包含 Mojo。因此,扩展非常适合指定公共插件接口的多个实现中的一个。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<build>
...
<extensions>
<extension>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-ftp</artifactId>
<version>1.0-alpha-3</version>
</extension>
</extensions>
...
</build>
</project>
reporting
<reporting>
元素包含与站点生成阶段相关的元素。某些 Maven 插件可以生成在 <reporting>
元素下定义和配置的报告,例如:生成 Javadoc 报告。与 <build>
元素配置插件的功能非常相似,<reporting>
元素具有相同的功能。明显的区别在于, <reporting>
元素在 <reportSet>
元素中配置目标,而不是在 <executions>
元素中对插件目标进行细粒度控制。更微妙的区别是, <reporting>
元素下插件的 <configuration>
可以用作 <build>
中插件的 <configuration>
,尽管相反的情况并非如此: <build>
中插件 <configuration>
不会影响 <reporting>
中的插件。
了解 <build>
元素的人对于 <reporting>
元素可能唯一不熟悉的是 Boolean 类型的 <excludeDefaults>
元素。此元素指示站点生成器排除默认情况下通常生成的报告。通过 site
构建周期生成站点时,左侧菜单中会显示一个 Project Info
部分,其中包含大量报告,例如 Project Team
报告或 Dependencies
列表报告。这些报告目标由 maven-project-info-reports-plugin
插件生成的。与其他插件一样,它也可能以下更详细的方式被抑制,从而有效地关闭项目信息报告。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<reporting>
<outputDirectory>${basedir}/target/site</outputDirectory>
<plugins>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>2.0.1</version>
<reportSets>
<reportSet></reportSet>
</reportSets>
</plugin>
</plugins>
</reporting>
...
</project>
另一个区别是 <plugin>
下的 <outputDirectory>
元素。在 <reporting>
下,输出目录默认为 ${basedir}/target/site
。
重要的是要记住,一个插件可能有多个目标。每个目标可能有一个单独的配置。<reportSets>
配置报告类插件的目标的执行,这听起来熟悉吧?关于构建的 <execution>
元素也说了同样的话,但有一个区别:不能将报告类插件的目标绑定到另一个阶段。
例如,假设我们想要配置要链接到 http://java.sun.com/j2se/1.5.0/docs/api/ 的 javadoc:javadoc
目标,但只有 javadoc 目标(而不是 maven-javadoc-plugin:jar
)。我们还希望将此配置传递给其子级,并将 <inherited>
设置为 true。 <reportSets>
类似于以下内容:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<reporting>
<plugins>
<plugin>
...
<reportSets>
<reportSet>
<id>sunlink</id>
<reports>
<report>javadoc</report>
</reports>
<inherited>true</inherited>
<configuration>
<links>
<link>http://java.sun.com/j2se/1.5.0/docs/api/</link>
</links>
</configuration>
</reportSet>
</reportSets>
</plugin>
</plugins>
</reporting>
...
</project>
在 <build>
的 <executions>
和 <reporting>
的 <reportSets>
之间,现在应该清楚它们存在的原因。POM 不仅必须有一种方法来配置插件,还必须配置这些插件的目标。这就是这些元素的用武之地,使 POM 以最细的粒度来控制其构建行为。
更多项目信息
有几个元素不影响构建,而是记录项目以方便开发人员。在生成项目网站时,这些元素中的许多元素用于填充项目详细信息。然而,像所有POM声明一样,插件可以将它们用于任何事情。以下是最简单的元素:
-
name
:除了artifactId
之外,项目往往有非正式的名字。Sun 的工程师们并没有将他们的项目称为java-1.5
,而只是称之为Tiger
。这里是设置该值的位置。 -
description
:项目的简短易读描述。尽管这不应取代正式的文档,但对 POM 的任何读者的快速评论总是有帮助的。 -
url
:项目的主页。 -
inceptionYear
:项目最初创建的年份。
licenses
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
<comments>A business-friendly OSS license</comments>
</license>
</licenses>
<licenses>
是定义如何以及何时使用项目(或项目的一部分)的法律文件。项目应该列出直接应用于此项目的许可证,而不是列出应用于项目依赖项的许可证。
-
name
、url
和comments
:是不言自明的,以前在其他上下文中也遇到过。建议使用 SPDX 标识符作为许可证名称。第四个许可要素是: -
distribution
:描述如何合法发布项目。这两种方法:repo
(可以从 Maven 存储库下载)或manual
(必须手动安装)。
organization
大多数项目由某种组织(企业、私人团体等)运行。这里是设置最基本信息的地方。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<organization>
<name>Codehaus Mojo</name>
<url>http://mojo.codehaus.org</url>
</organization>
</project>
developers
所有项目都由一个人在某个时间创建的文件组成。与围绕项目的其他系统一样,参与项目的人员也与项目有利害关系。开发人员可能是项目核心开发的成员。请注意,尽管一个组织可能有许多开发人员(程序员)作为成员,但将他们全部列为开发人员并不是一种好的形式,而只列出那些直接负责代码的人。一个很好的经验法则是,如果不应该因为项目联系此人,他们就不需要在这里列出。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<developers>
<developer>
<id>jdoe</id>
<name>John Doe</name>
<email>jdoe@example.com</email>
<url>http://www.example.com/jdoe</url>
<organization>ACME</organization>
<organizationUrl>http://www.example.com</organizationUrl>
<roles>
<role>architect</role>
<role>developer</role>
</roles>
<timezone>America/New_York</timezone>
<properties>
<picUrl>http://www.example.com/jdoe/pic</picUrl>
</properties>
</developer>
</developers>
...
</project>
-
id
、name
、email
:这些对应于开发人员的 id(可能是整个组织中的某个唯一 id)、开发人员的姓名和电子邮件地址。 -
organization
、organizationUrl
:正如您可能猜到的,这是开发人员的组织名称和 URL。 -
roles
:角色应指定该人员负责的标准操作。就像一个人可以戴很多帽子一样,一个人可以扮演多个角色。 -
timezone
:一个有效的时区 ID,如America/New_York
或Europe/Berlin
,或以小时(和分数)为单位的数字偏移量(基于开发人员居住的 UTC 时间)例如-5
或+1
。时区 ID 是首选,因为它们不受 DST 和时区偏移的影响。有关官方时区数据库和维基百科中的列表,请参考 IANA。 -
properties
:这个元素是关于这个人的任何其他属性所在的位置。例如,指向个人图像或即时通讯方式的链接。不同的插件可能会使用这些属性,也可能只是针对阅读 POM 的其他开发人员。
contributors
<contributors>
就像开发人员一样,在项目的生命周期中扮演着辅助角色。也许贡献者发送了一个 bug 修复,或者添加了一些重要的文档。一个健康的开源项目可能会有比开发者更多的贡献者。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<contributors>
<contributor>
<name>Noelle</name>
<email>some.name@gmail.com</email>
<url>http://noellemarie.com</url>
<organization>Noelle Marie</organization>
<organizationUrl>http://noellemarie.com</organizationUrl>
<roles>
<role>tester</role>
</roles>
<timezone>America/Vancouver</timezone>
<properties>
<gtalk>some.name@gmail.com</gtalk>
</properties>
</contributor>
</contributors>
...
</project>
<contributors>
包含的元素集与没有 id 元素的开发人员包含的元素集相同。
环境设置
issueManagement
<issueManagement>
定义了所使用的缺陷跟踪系统(Bugzilla、TestTrack、ClearQuest 等)。虽然没有什么可以阻止插件将这些信息用于某些事情,但它主要用于生成项目文档。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<issueManagement>
<system>Bugzilla</system>
<url>http://127.0.0.1/bugzilla/</url>
</issueManagement>
...
</project>
持续集成管理
在过去几年中,基于触发器或计时器(例如,每小时或每天)的持续集成构建系统越来越受到手动构建者的青睐。随着构建系统变得更加标准化,运行触发器的系统也会触发这些构建。尽管大多数配置取决于所使用的特定程序(Continuum、Cruise Control,制等),但也有一些配置可能发生在 POM 内。Maven 在 <notifier>
元素中捕获了一些重复设置。<notifier>
是通知人们某些构建状态的方式。在以下示例中,此 POM 正在设置邮件类型的通知程序,并将电子邮件地址配置为用于指定的触发器 sendOnError
、sendOnFailure
,而不是 sendOnSuccess
或 sendOnWarning
。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<ciManagement>
<system>continuum</system>
<url>http://127.0.0.1:8080/continuum</url>
<notifiers>
<notifier>
<type>mail</type>
<sendOnError>true</sendOnError>
<sendOnFailure>true</sendOnFailure>
<sendOnSuccess>false</sendOnSuccess>
<sendOnWarning>false</sendOnWarning>
<configuration><address>continuum@127.0.0.1</address></configuration>
</notifier>
</notifiers>
</ciManagement>
...
</project>
邮件列表
邮件列表是与人们就项目保持联系的一个很好的工具。大多数邮件列表是为开发人员和用户提供的。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<mailingLists>
<mailingList>
<name>User List</name>
<subscribe>user-subscribe@127.0.0.1</subscribe>
<unsubscribe>user-unsubscribe@127.0.0.1</unsubscribe>
<post>user@127.0.0.1</post>
<archive>http://127.0.0.1/user/</archive>
<otherArchives>
<otherArchive>http://base.google.com/base/1/127.0.0.1</otherArchive>
</otherArchives>
</mailingList>
</mailingLists>
...
</project>
-
subscribe
、unsubscribe
:这些元素指定用于执行相关操作以订阅上面的用户列表的电子邮件地址。要订阅上面的用户列表,用户将向user-subscribe@127.0.0.1
发送电子邮件。 -
archive
:此元素指定旧邮件列表电子邮件存档的 url(如果存在)。如果存在镜像存档,则可以在<otherArchives>
下指定它们。 -
post
:用于向邮件列表发送邮件的电子邮件地址。请注意,并非所有邮件列表都能够发送到(例如构建失败列表)。
SCM
SCM(Software Configuration Management,也称为 Source Code/Control Management ,或简而言之就是版本控制)是任何健康项目的一个组成部分。如果您的 Maven 项目使用 SCM 系统,那么您可以在这里将该信息放入 POM 中。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<scm>
<connection>scm:svn:http://127.0.0.1/svn/my-project</connection>
<developerConnection>scm:svn:https://127.0.0.1/svn/my-project</developerConnection>
<tag>HEAD</tag>
<url>http://127.0.0.1/websvn/my-project</url>
</scm>
...
</project>
-
connection
、developerConnection
:这两个连接元素表示如何通过 Maven 连接到版本控制系统。如果连接需要读访问权限,Maven 才能找到源代码。如果需要更新,则developerConnection
需要提供写访问权限的连接。Maven 孵化出了另一个名为Maven SCM
的项目,它为希望实现它的任何 SCM 创建了一个公共 API。最受欢迎的是 CVS 和 Subversion,但是,其他受支持的 SCM 的列表也在不断增加。所有 SCM 连接都是通过一般的 URL 结构进行的。scm:[provider]:[provider_specific]
其中
provider
是 SCM 系统的类型。例如,连接到 CVS 存储库可能如下所示:scm:cvs:pserver:127.0.0.1:/cvs/root:my-project
tag
:指定此项目所在的标记。HEAD
(意思是 SCM 根)应该是默认值。url
:可公开可浏览的存储库。例如,通过 ViewCVS。
SCM URL 的常规格式为:scm:<scm_provider><delimiter><provider_specific_part>
。可以使用冒号 :
作为分隔符,或者,如果您想将冒号用于其中一个变量(例如 Windows 路径),则可以使用管道 |
。以下是已经完全实现的 SCM:
例如,对于 Git,对于下面的所有 URL,我们使用冒号 :
作为分隔符。如果对其中一个变量(例如 Windows 路径)使用冒号,则使用管道 |
作为分隔符。端口的分隔符在任何情况下都必须是冒号,因为这部分是在 git URL 规范中指定的。见 git-fetch
的手册。
scm:git:git://server_name[:port]/path_to_repository
scm:git:http://server_name[:port]/path_to_repository
scm:git:https://server_name[:port]/path_to_repository
scm:git:ssh://server_name[:port]/path_to_repository
scm:git:file://[hostname]/path_to_repository
示例
scm:git:git://github.com/path_to_repository
scm:git:http://github.com/path_to_repository
scm:git:https://github.com/path_to_repository
scm:git:ssh://github.com/path_to_repository
scm:git:file://localhost/path_to_repository
在某些情况下,读写操作必须使用不同的 URL。例如,如果通过 http 协议执行获取,但只能通过 ssh 写入存储库,则可能发生这种情况。在这种情况下,两个URL 都可以写入 <developerConnection>
元素中。fetch URL 必须以 [fetch=]
为前缀,push URL 必须以 [push=]
为前缀。例如:
<developerConnection>scm:git:[fetch=]http://mywebserver.org/path_to_repository[push=]ssh://username@otherserver:8898/~/repopath.git</developerConnection>
从 Maven SCM 1.3 版开始,我们假设远程仓库中的分支名称与当前本地分支的名称相同。因此,无论何时调用必须访问远程仓库的 maven-scm
操作,您都应该处于该分支上。换句话说:如果没有手动指定分支,那么每个 git-fetch
、git-pull
、git-push
等将始终在远程仓库中的分支上工作,该分支与当前本地分支具有相同的分支名称:
git push pushUrl currentBranch:currentBranch
provider 程序配置在 ${user.home}/.scm/git-settings.xml
中定义。如下所示:
<git-settings xmlns="http://maven.apache.org/SCM/GIT/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SCM/GIT/1.1.0 http://maven.apache.org/xsd/scm-git-1.1.0.xsd">
<revParseDateFormat/>
<traceGitCommand/>
<gitCommand/>
<commitNoVerify/>
</git-settings>
-
revParseDateFormat
:changelog
命令允许的 git 格式,默认为yyyy-MM-dd HH:mm:ss
。 -
traceGitCommand
:跟踪 git 命令的执行。可以是1
、2
、true
或文件位置。默认为空字符串。 -
gitCommand
:git 可执行文件的当前名称。默认为git
。 -
commitNoVerify
:使用选项--no-verify
(可以防止 cygwin 出现尾随空格问题)。默认为false
。
Prerequisites
POM 可能有某些先决条件才能正确执行。例如,Maven 2.0.3 中可能有一个补丁,您需要它来使用 sftp 进行部署。在 Maven 3 中,使用 Maven Enforcer 插件的 requireMavenVersion
规则或其他规则检查其他方面。
在 Maven 2 中,这里是您为构建提供先决条件的地方:如果不满足这些先决条件,Maven 甚至在开始构建之前都会失败。POM 4.0 中唯一作为先决条件存在的元素是 <maven>
元素,它具有最小的版本号。它是用 Maven 2 检查的,不再出现在 Maven 3 中。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<prerequisites>
<maven>2.0.6</maven>
</prerequisites>
...
</project>
Repositories
Repositories 是遵循 Maven 仓库目录布局的工件集合。为了成为 Maven 仓库工件,项目的 POM 文件的路径必须为 $BASE_REPO/groupId/artifactId/version/artifactId-version.pom
,其中,$BASE_REPO
可以是本地仓库的文件目录,也可以是远程仓库中的基本 URL,其余的路径结构都是相同的。
仓库作为收集和存储工件的场所存在。每当项目依赖某个工件时,Maven 都会首先尝试使用指定工件的本地副本。如果本地仓库中不存在该工件,它将尝试从远程仓库下载。POM 中的 <repositories>
元素指定要搜索的备用仓库。
仓库是 Maven 社区最强大的功能之一。默认情况下,Maven 在以下位置搜索中央仓库:https://repo.maven.apache.org/maven2/,可以在pom.xml 的 repositories
元素中配置其他存储库。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<repositories>
<repository>
<releases>
<enabled>false</enabled>
<updatePolicy>always</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
<checksumPolicy>fail</checksumPolicy>
</snapshots>
<name>Nexus Snapshots</name>
<id>snapshots-repo</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<layout>default</layout>
</repository>
</repositories>
<pluginRepositories>
...
</pluginRepositories>
...
</project>
-
releases
、snapshots
:这些是针对每种工件类型的策略:版本(releases)或快照(snapshots)。有了这两个集合,POM 就可以在单仓库中独立地更改每种类型的策略。例如,您可能决定仅启用快照下载,可能是出于开发目的。 -
enabled
:表示该仓库是否已启用相应类型(releases
或snapshots
)的工件,为true
或false
。 -
updatePolicy
:该元素指定尝试更新的频率。Maven 将本地 POM 的时间戳(存储在仓库的 Maven 元数据文件中)与远程 POM 进行比较。可以为:always
、daily
、interval:X
((其中 X 是以分钟为单位的整数)、never
。 -
checksumPolicy
:当 Maven 将文件部署到仓库时,它也会部署相应的校验和(checksum)文件。在遇到校验和文件丢失或不一致时,您可以选择使用ignore
、fail
或warn
之一。 -
layout
:在上面对存储库的描述中,提到它们都遵循一个通用的布局。这基本上是正确的。Maven 2 引入的布局是 Maven 2 和 3 使用的存储库的默认布局;然而,Maven 1.x 有一个不同的布局。使用此元素可以指定默认元素还是传统元素。
Plugin Repositories
仓库是两种主要类型工件的所在地。第一个工件是作为依赖项的工件,中央仓库中大多数工件都是此类型。另一类工件就是插件,Maven 插件本身就是一种特殊类型的工件。正因为如此,插件仓库与依赖项仓库可能是分开的(然后,现实中两者分开的并不多)。在任何情况下,<pluginRepositories>
元素的结构都类似于 <repositories>
元素。
distributionManagement
<distributionManagement>
的作用就是管理在整个构建过程中生成的工件和支持工件的部署。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<distributionManagement>
...
<downloadUrl>http://mojo.codehaus.org/my-project</downloadUrl>
<status>deployed</status>
</distributionManagement>
...
</project>
-
downloadUrl
:该 URL 是 Maven 上传部署该 POM 工件的仓库地址,其他用户可以从该地址下载该 POM 的工件。 -
status
:就像鸟巢里的小鸟,status 永远不应该被人手触摸!原因是 Maven 将在项目传输到仓库时将设置项目的状态。其有效类型如下所示。-
none
:无特殊状态。这是 POM 的默认设置。 -
converted
:仓库的管理员将此 POM 从早期版本转换为 Maven 2。 -
partner
:此工件已与伙伴仓库同步。 -
deployed
:到目前为止是最常见的状态,这意味着该工件是通过 Maven 2 或 3 部署的。这是使用命令行部署阶段手动部署时得到的结果。 -
verified
:该项目已验证,应视为已完成。
-
Repository
<repositories>
元素在 POM 中指定了 Maven 下载远程工件以供当前项目使用的位置和方式,而 <distributionManagement>
指定了该项目在部署时到达远程仓库的位置(以及方式)。如果未定义 <snapshotRepository>
,则 <repository>
元素将用于快照分发。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<distributionManagement>
<repository>
<uniqueVersion>false</uniqueVersion>
<id>corp1</id>
<name>Corporate Repository</name>
<url>scp://repo/maven2</url>
<layout>default</layout>
</repository>
<snapshotRepository>
<uniqueVersion>true</uniqueVersion>
<id>propSnap</id>
<name>Propellors Snapshots</name>
<url>sftp://propellers.net/maven</url>
<layout>legacy</layout>
</snapshotRepository>
...
</distributionManagement>
...
</project>
-
id
、name
:该id
用于在众多仓库中唯一标识该仓库,并且该name
是易读的形式。 -
uniqueVersion
:采用true
或false
值来表示部署到此仓库的工件是否应该获得唯一的版本号,或者使用地址中定义的版本号。 -
url
:这是<repository>
元素的核心。它指定了用于将构建工件(以及 POM 文件和校验和数据)传输到存储库的位置和传输协议。 -
layout
:这些类型和用途与存储库元素中定义的布局元素相同。它们是默认的和遗留的。
Site Distribution
<distributionManagement>
元素不仅负责发布到仓库,还负责定义如何部署项目的站点和文档。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<distributionManagement>
...
<site>
<id>mojo.website</id>
<name>Mojo Website</name>
<url>scp://beaver.codehaus.org/home/projects/mojo/public_html/</url>
</site>
...
</distributionManagement>
...
</project>
-
id
、name
、url
:这些元素与上面<distributionManagement>
中的repository
元素中对应元素类似。
Relocation
<project xmlns="http://maven.apache.org/POM/4.0.0"1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<distributionManagement>
...
<relocation>
<groupId>org.apache</groupId>
<artifactId>my-project</artifactId>
<version>1.0</version>
<message>We have moved the Project under Apache</message>
</relocation>
...
</distributionManagement>
...
</project>
项目不是静态的,它们是有生命的东西(或垂死的东西,视情况而定)。随着项目的发展,一个常见的现象是,它们可能会被迫搬到更合适的地方。例如,当您的下一个非常成功的开源项目在 Apache 保护伞下运行时,最好让用户知道该项目将被重命名为 org.apache:my-project:1.0
。除了指定新地址外,提供一条解释原因的消息也是一种很好的形式。
Profiles
POM 4.0 的一个新特性是项目能够根据构建环境更改设置。<profiles>
元素包含可选 <activation>
(profile 触发器)和一组对 POM 的更改(如果该 profile 已激活)。例如,为测试环境构建的项目与与最终部署的项目存在许多不同的属性配置(诸如数据库连接信息、关联服务的访问地址等)。或者,依赖关系可以根据所使用的 JDK 版本从不同的仓库中提取。<profiles>
元素如下所示:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<profiles>
<profile>
<id>test</id>
<activation>...</activation>
<build>...</build>
<modules>...</modules>
<repositories>...</repositories>
<pluginRepositories>...</pluginRepositories>
<dependencies>...</dependencies>
<reporting>...</reporting>
<dependencyManagement>...</dependencyManagement>
<distributionManagement>...</distributionManagement>
</profile>
</profiles>
</project>
激活
激活是 profile
的关键。profile
的威力来自于它仅在特定情况下修改基本 POM 的能力。这些情况通过 <activation>
元素指定。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<profiles>
<profile>
<id>test</id>
<activation>
<activeByDefault>false</activeByDefault>
<jdk>1.5</jdk>
<os>
<name>Windows XP</name>
<family>Windows</family>
<arch>x86</arch>
<version>5.1.2600</version>
</os>
<property>
<name>sparrow-type</name>
<value>African</value>
</property>
<file>
<exists>${basedir}/file2.properties</exists>
<missing>${basedir}/file1.properties</missing>
</file>
</activation>
...
</profile>
</profiles>
</project>
在 Maven 3.2.2 之前,当满足一个或多个指定标准时,激活发生。遇到第一个肯定结果时,处理停止,配置文件标记为活动。因为 Maven 3.2.2 激活发生在满足所有指定标准的情况下。
-
jdk
:activation
在jdk
元素中有一个内置的、以 Java 为中心的检查。如果执行 Maven 构建的 jdk 版本与jdk
元素值中指定的前缀匹配,则该 profile 将激活。在上面的示例中,1.5.0_06 可以匹配。还支持匹配范围。有关支持范围的更多详细信息,请参阅 maven-enforcer-plugin 插件。 -
os
:该元素可以定义上面所示的一些特定于操作系统的属性。有关操作系统值的更多详细信息,请参阅 maven-enforcer-plugins RequireOS 规则。 -
property
:如果 Maven 检测到对应name=value
对的系统属性或命令行属性(一个可以由${name}
在 POM 中取消引用的值),则该 profile 将被激活。 -
file
:最后,一个给定的文件名可能通过文件的existence
或missing
来激活该 profile。注意:此元素的插值仅限于${basedir}
、系统属性和请求属性。
<activation>
元素不是激活 profile 的唯一方式。settings.xml
文件的 activeProfile
元素可能包含 profile 的 id。也可以通过在命令行的 -P
标志(例如 -P codecoverage
)后面的逗号分隔列表显式激活。
要查看哪个配置文件将在特定构建中激活,请使用 maven-help-plugin。
mvn help:active-profiles