情况是,我有两个结构相同的 Maven 多模块项目:
Parent - Module 1 - Module 2
当我构建项目 1 时,我看到先构建父级(顺序是 parent->module1->module2)。但是对于项目 2,最后构建父级(顺序为模块 1->模块2->父级)。为什么这两个项目有不同的构建顺序?此外,如何手动控制构建顺序?
更新1:两个父项目都是没有源代码的简单POM项目,所以我无法根据依赖图解释构建顺序。
更新 2:除了 GAV 和子模块名称之外,父 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" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>parent-group-id</groupId> <artifactId>parent-artifact-id</artifactId> <version>parent-version</version> <packaging>pom</packaging> <name>parent-name</name> <modules> <module>module-1</module> <module>module-2</module> </modules> </project>
构建顺序由 Maven 反应器确定,这是一种通过对项目进行排序来自动确保多模块构建的正确构建顺序的机制。
请参阅 the official documentation 了解其工作原理。
它说:
对项目进行排序时遵循以下关系: 项目依赖于构建中的另一个模块 插件声明,其中插件是构建中的另一个模块 插件依赖于构建中的另一个模块 构建扩展声明对构建中的另一个模块在元素中声明的顺序(如果没有其他规则适用)
您无法手动控制构建顺序。如果要更改顺序,则必须对影响反应器排序顺序的项目进行更改。
在较高级别上,构建顺序基于模块依赖关系图的 topological sort。
编辑:问你问题。我知道项目 1 包含两个模块,项目 2 也是如此。但是项目 2 中的模块是否明确将“父”pom 声明为父节点?我在想也许你的项目 1 模块明确声明了父 pom,而项目 2 模块没有。这意味着项目 2“父级”根本不是真正的父级,因此不必在模块之前构建。无论如何,这是我的猜测。
我最近在使用 CentOS 7 时遇到了这个问题。我将 Maven 从 3.0.5 更新到 3.5.3,问题已经解决。如果有人也有这个问题,您可以先尝试这样做。
概括
为什么?
Maven 使用 <module>
声明来确定要包含在当前运行中的模块列表。 Maven 使用 <parent>
声明为每个包含的模块 生成有效的 POM,然后用于执行该模块的构建。
在 Project 1 中,module1
和 module2
中的每一个都在 <parent>
部分指定了 parent
模块。在 Project 2 中,module1
和 module2
均未在 <parent>
部分指定 parent
模块。
如何手动控制构建顺序?
通过更改模块之间的依赖关系,包括 <parent>
、<dependencies>
、插件依赖关系等,如 the official documentation 中所述。
另请查看 Introduction to the POM 以了解有关聚合和继承的讨论。
细节
从聚合(<module>
声明)和继承(<parent>
声明)的角度来看,Maven 中有 4 种可能的模块关系场景。
让我们假设一个带有包装 pom
的模块 A
和一个模块 B
(包装无关紧要)。模块 A
可以是父模块或聚合器。模块 B
可以将 A
引用为父级,也可以不引用。
情况1:
A
和 B
都不会相互引用 - 模块是独立的。 Maven 无法控制构建顺序,每个模块都是独立构建的,具有手动构建顺序控制。
案例二:
模块 A
包含 <modules>
中的模块 B
。模块 B
不声明 A
为父模块。在这种情况下,模块 A
是模块 B
的聚合器。
Maven 调用 mvn -f A/pom.xml 将首先构建 B,然后构建 A。
Maven 调用 mvn -f B/pom.xml 将仅构建 B。
案例3:
模块 A
包含 <modules>
中的模块 B
。模块 B
声明 A
为父模块。在这种情况下,模块 A
既是模块 B
的父级也是聚合器(反应器)。
Maven 调用 mvn -f A/pom.xml 将首先构建 A,然后构建 B。
Maven 调用 mvn -f B/pom.xml 将仅构建 B,但将使用 A 的 POM 通过从本地存储库或遵循 /project/parent/relativePath 解析它来生成 B 的有效 POM。
案例4:
模块 A
不包括 <modules>
中的模块 B
。模块 B
声明 A
为父模块。在这种情况下,模块 A
是模块 B
的父级。
Maven 调用 mvn -f A/pom.xml 将仅构建 A。
Maven 调用 mvn -f B/pom.xml 将仅构建 B,但将使用 A 的 POM 通过从本地存储库或遵循 /project/parent/relativePath 解析它来生成 B 的有效 POM。