项目对象模型或POM是Maven中的基本工作单元。它是一个XML文件,包含Maven用于构建项目的项目信息和配置细节。它包含大多数项目的默认值。例如构建目录, target
目录,源代码目录 src/main/java
,测试源代码目录 src/test/java
等。在执行任务或目标时,Maven在当前目录中查找pom文件。读取pom,获取需要的配置信息,然后执行目标。
在pom中可以指定的信息有项目依赖项、执行的插件或目标、构建配置文件等。还可以指定项目版本、描述、开发人员、邮件列表等其他信息。
Super POM是Maven的默认POM。除非显示设置,否则所有的pom都扩展Super POM。这意味着Super POM中指定的配置,将由我们创建的项目的pom继承。Super POM的代码片段参照 官网 。
POM的配置的最低要求如下:
示例:
<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中合并的元素如下:
Super POM是项目继承的一个例子,但是我们也可以通过指定pom中的parent标签来引入自己的父POM,如下面的示例所示。
例如,我们重用以前的项目com.mycompany.app:my-app:1,并且引入另一个项目com.mycompany.app:my-module:1。
<project> <modelVersion>4.0.0</modelVersion> <groupId>com.mycompany.app</groupId> <artifactId>my-module</artifactId> <version>1</version> </project>
让我们指定他们的目录结构如下:
. |-- 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如下:
<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标签:
<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>
尽管如果父项目已经安装到本地存储库中,或者在特定的目录结构中(模块的目录为父POM的子目录),上面的配置方式是可行的。但是如果父项目还没安装,并且目录结构与下面的示例一样,该怎么办呢?
. |-- my-module | `-- pom.xml `-- parent `-- pom.xml
要处理这个目录结构,需要将 <relativePath>
标签添加到parent部分:
<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和目录结构:
<project> <modelVersion>4.0.0</modelVersion> <groupId>com.mycompany.app</groupId> <artifactId>my-app</artifactId> <version>1</version> </project>
<project> <modelVersion>4.0.0</modelVersion> <groupId>com.mycompany.app</groupId> <artifactId>my-module</artifactId> <version>1</version> </projec
. |-- my-module | `-- pom.xml `-- pom.xml
如果要将my-module聚合到my-app中,只需要修改my-app即可。
<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 上运行。另外,一些命令以不同的方式处理命令聚合。
同样,如果我们将目录结构更改为以下形式会怎样呢?
. |-- my-module | `-- pom.xml `-- parent `-- pom.xml
父POM如何指定它的模块?
与之前差不多,指定模块的路径:
<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>
如果有几个Maven项目,并且他们都有相似的配置,那么可以通过提取这些相似的配置并创建父项目来重构项目。因此,需要做的是让Maven项目继承父项目,然后这些配置将适用于所有项目。
如果有一组一起构建或处理的项目,可以创建一个父项目,并声明这些项目是父项目的模块。通过这样,我们只需要构建父类,其他的会随之构建好。
当然,我们可以同时拥有项目继承和聚合。这意味着,可以让模块们指定一个父项目;同时,让该父项目指定那些项目为它的模块。需要遵循如下三条规则:
依旧使用上面的POM:
<project> <modelVersion>4.0.0</modelVersion> <groupId>com.mycompany.app</groupId> <artifactId>my-app</artifactId> <version>1</version> </project>
<project> <modelVersion>4.0.0</modelVersion> <groupId>com.mycompany.app</groupId> <artifactId>my-module</artifactId> <version>1</version> </project>
目录结构如下:
. |-- my-module | `-- pom.xml `-- parent `-- pom.xml
同时进行项目继承和聚合
<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>
<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
变量,可以这样引用:
<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
来定制。格式如下:
<project> ... <properties> <maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'</maven.build.timestamp.format> </properties> ... </project>
可以将任何属性定义为变量,考虑如下的例子:
<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>