bboyjing's blog

Maven学习笔记三【POM文件】

本节来学习下Maven的pom文件。

什么是POM文件

项目对象模型或POM是Maven中的基本工作单元。它是一个XML文件,包含Maven用于构建项目的项目信息和配置细节。它包含大多数项目的默认值。例如构建目录,target目录,源代码目录src/main/java,测试源代码目录src/test/java等。在执行任务或目标时,Maven在当前目录中查找pom文件。读取pom,获取需要的配置信息,然后执行目标。

在pom中可以指定的信息有项目依赖项、执行的插件或目标、构建配置文件等。还可以指定项目版本、描述、开发人员、邮件列表等其他信息。

Super POM

Super POM是Maven的默认POM。除非显示设置,否则所有的pom都扩展Super POM。这意味着Super POM中指定的配置,将由我们创建的项目的pom继承。Super POM的代码片段参照官网

Minimal POM

POM的配置的最低要求如下:

  • project root
  • modelVersion - 需要被设置成4.0.0
  • groupId - 项目组织的ID
  • artifactId - 项目的ID
  • version - 项目的版本号

示例:

1
2
3
4
5
6
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
</project>

POM需要配置其groupId、artifactId和version。这三个值构成了项目的完全限定名,以<groupId>:<artifactId>:<version>的形式出现。对于上面的例子,它的完全限定名是“com.mycompany.app:my-app:1”。

如果没有指定配置细节,Maven将使用它们的默认值,这些默认值之一是打包类型。每个Maven项目都有一种打包类型如果POM中没有指定它,那么将使用默认值“jar”。

此外,我们看到最小POM中没有指定存储库。如果使用最小POM构建项目,它将继承Super POM中的存储库配置。因此,当Maven看到最小POM中的依赖项时,它知道这些依赖项将从Super POM指定的路径http://repo.maven.apache.org/maven2 下载。

项目继承

POM中合并的元素如下:

  • dependencies
  • developers and contributors
  • plugin lists (including reports)
  • plugin executions with matching ids
  • plugin configuration
  • resources

Super POM是项目继承的一个例子,但是我们也可以通过指定pom中的parent标签来引入自己的父POM,如下面的示例所示。

Example 1

场景

例如,我们重用以前的项目com.mycompany.app:my-app:1,并且引入另一个项目com.mycompany.app:my-module:1。

1
2
3
4
5
6
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-module</artifactId>
<version>1</version>
</project>

让我们指定他们的目录结构如下:

1
2
3
4
.
|-- my-module
| `-- pom.xml
`-- pom.xml

注意:my-module/pom.xml是com.mycompany.app:my-module:1的POM,而pom.xml是com.mycompany.app:my-app:1的POM。

解决方案

现在,我们将com.mycompany.app:my-app:1转变为com.mycompany.app:my-module:1的父项目,修改com.mycompany.app:my-module:1’的pom.xml如下:

1
2
3
4
5
6
7
8
9
10
11
<project>
<parent>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-module</artifactId>
<version>1</version>
</project>

注意,我们现在有了一个添加的部分,parent标签。这个部分允许指定哪个项目是我们POM的父项目,我们通过指定父POM的完全限定名来实现这一点。过这个设置,我们的模块现在可以继承父POM的一些属性。

或者,如果我们希望模块的groupId或version与父模块相同,可以删除模块POM中的groupId和version标签:

1
2
3
4
5
6
7
8
9
<project>
<parent>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>my-module</artifactId>
</project>

Example 2

场景

尽管如果父项目已经安装到本地存储库中,或者在特定的目录结构中(模块的目录为父POM的子目录),上面的配置方式是可行的。但是如果父项目还没安装,并且目录结构与下面的示例一样,该怎么办呢?

1
2
3
4
5
.
|-- my-module
| `-- pom.xml
`-- parent
`-- pom.xml
解决方案

要处理这个目录结构,需要将<relativePath>标签添加到parent部分:

1
2
3
4
5
6
7
8
9
10
<project>
<parent>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>my-module</artifactId>
</project>

顾名思义,它是模块pom.xml对于父pom.xml的相对路径。

项目聚合

项目聚合类似于项目继承。但它不是从模块指定父POM,而是从父POM指定模块。通过这样,父项目知道了它的模块,如果对父项目调用Maven命令,那么命令也会执行到父项目的各个模块。要进行项目聚合,需要做如下:

  • 将父项目的打包类型改为pom
  • 在父POM中指定模块

Example 3

场景

沿用上面的POM和目录结构:

com.mycompany.app:my-app:1的POM

1
2
3
4
5
6
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
</project>

com.mycompany.app:my-module:1的POM

1
2
3
4
5
6
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-module</artifactId>
<version>1</version>
</projec

目录结构如下:

1
2
3
4
.
|-- my-module
| `-- pom.xml
`-- pom.xml
The Solution

如果要将my-module聚合到my-app中,只需要修改my-app即可。

1
2
3
4
5
6
7
8
9
10
11
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
<packaging>pom</packaging>
<modules>
<module>my-module</module>
</modules>
</project>

如上,增加了<packaging><modules>部分。现在,每当Maven命令处理com.mycompany.app:my-app:1时,相同的命令会在com.mycompany.app:my-module:1 上运行。另外,一些命令以不同的方式处理命令聚合。

Example 4

场景

同样,如果我们将目录结构更改为以下形式会怎样呢?

1
2
3
4
5
.
|-- my-module
| `-- pom.xml
`-- parent
`-- pom.xml

父POM如何指定它的模块?

解决方案

与之前差不多,指定模块的路径:

1
2
3
4
5
6
7
8
9
10
11
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
<packaging>pom</packaging>
<modules>
<module>../my-module</module>
</modules>
</project>

项目继承 VS 项目聚合

如果有几个Maven项目,并且他们都有相似的配置,那么可以通过提取这些相似的配置并创建父项目来重构项目。因此,需要做的是让Maven项目继承父项目,然后这些配置将适用于所有项目。

如果有一组一起构建或处理的项目,可以创建一个父项目,并声明这些项目是父项目的模块。通过这样,我们只需要构建父类,其他的会随之构建好。

当然,我们可以同时拥有项目继承和聚合。这意味着,可以让模块们指定一个父项目;同时,让该父项目指定那些项目为它的模块。需要遵循如下三条规则:

  • 在每个子POM中指定它们的父POM是谁
  • 将父pom的打包类型更改为值“pom”
  • 在父POM中指定其模块的目录

Example 5

场景

依旧使用上面的POM:

com.mycompany.app:my-app:1的POM

1
2
3
4
5
6
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
</project>

com.mycompany.app:my-module:1的POM

1
2
3
4
5
6
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-module</artifactId>
<version>1</version>
</project>

目录结构如下:

1
2
3
4
5
.
|-- my-module
| `-- pom.xml
`-- parent
`-- pom.xml
解决方案

同时进行项目继承和聚合

com.mycompany.app:my-app:1的POM

1
2
3
4
5
6
7
8
9
10
11
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
<packaging>pom</packaging>
<modules>
<module>../my-module</module>
</modules>
</project>

com.mycompany.app:my-module:1的POM

1
2
3
4
5
6
7
8
9
10
<project>
<parent>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>my-module</artifactId>
</project>

注意:Profile的继承与用于POM本身的继承策略相同。

项目插值和变量

Maven鼓励的实践之一是不要重复自己的操作。但是,在某些情况下,您需要在几个不同的位置使用相同的值。为了帮助确保值只指定一次,Maven允许在POM中使用自己的变量和预定义的变量。

例如,访问project.version变量,可以这样引用:

1
<version>${project.version}</version>

需要注意的一个因素是,这些变量是在继承之后处理的。这意味着,如果父项目使用变量,那么最终使用的将是它在子项目中的定义,而不是父项目中的定义。

可用变量

项目模型变量

模型中单个值元素的任何字段都可以作为变量引用。例如,${project.groupId}${project.version}${project.build.sourceDirectory}等。参阅POM引用以查看完整的属性列表。

特殊变量
属性 解释
project.basedir 当前项目所在目录
project.baseUri 当前项目所在目录,表示为URI。>=Maven2.1
maven.build.timestamp 表示构建开始的时间戳。>=Maven2.1.0-M1

构建时间戳的格式可以通过声明property maven.build.timestamp来定制。格式如下:

1
2
3
4
5
6
7
<project>
...
<properties>
<maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'</maven.build.timestamp.format>
</properties>
...
</project>
属性

可以将任何属性定义为变量,考虑如下的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<project>
...
<properties>
<mavenVersion>2.1</mavenVersion>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-artifact</artifactId>
<version>${mavenVersion}</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-project</artifactId>
<version>${mavenVersion}</version>
</dependency>
</dependencies>
...
</project>