在本教程中,我们将学习如何使用 Maven 构建多模块项目。
首先,我们将讨论什么是多模块项目,并看看遵循这种方法的好处。然后我们将设置我们的示例项目。有关 Maven 的良好介绍,请查看本教程。
多模块项目由管理一组子模块的聚合器 POM 构建。在大多数情况下,聚合器位于项目的根目录中,并且必须具有 pom类型的打包。
子模块是常规的 Maven 项目,它们可以单独构建,也可以通过聚合器 POM 构建。
通过聚合器 POM 构建项目,每个具有与pom不同的打包类型的项目都会生成一个构建的存档文件。
使用这种方法的显着优势是 我们可以减少重复。
假设我们有一个由几个模块组成的应用程序,一个前端模块和一个后端模块。现在想象一下我们对它们进行处理并更改功能,这会影响它们。在这种情况下,如果没有专门的构建工具,我们将不得不分别构建两个组件或编写脚本来编译代码、运行测试并显示结果。然后,当我们在项目中获得更多模块后,它会变得更难管理和维护。
在现实世界中,项目可能需要某些 Maven 插件在构建生命周期中执行各种操作,共享依赖项和配置文件,以及包含其他BOM 项目。
因此,当利用多模块时,我们可以在一个命令中构建应用程序的模块,如果顺序很重要,Maven 会为我们解决。我们还可以与其他模块共享大量配置。
Maven 以每个 pom.xml 文件都具有隐式父 POM的方式支持继承。它称为 Super POM ,可以位于 Maven 二进制文件中。这两个文件由 Maven 合并,形成 Effective POM。
我们可以创建自己的 pom.xml 文件,作为父项目。然后我们可以在其中包含所有具有依赖关系的配置,并将其设置为我们子模块的父模块,以便它们继承它。
除了继承之外,Maven 还提供了聚合的概念。利用此功能的父 POM 称为聚合 POM 。基本上,这种 POM在其 pom.xml 文件中显式声明其模块。
子模块或子项目是从父 POM 继承的常规 Maven 项目。正如我们已经知道的,继承让我们与子模块共享配置和依赖关系。但是,如果我们想一次性构建或发布我们的项目,我们必须在父 POM 中明确声明我们的子模块。最终,我们的父 POM 将成为父 POM,以及聚合 POM。
现在我们了解了 Maven 的子模块和层次结构,让我们构建一个示例应用程序来演示它们。我们将使用 Maven 的命令行界面来生成我们的项目。
这个应用程序将包含三个模块,它们代表:
由于我们将专注于 Maven,因此这些服务的实现将保持未定义。
首先,让我们创建一个父项目:
mvn archetype:generate -DgroupId=com.baeldung -DartifactId=parent-project
生成父级后,我们必须打开 位于父级目录中的pom.xml文件并将包装添加为pom:
<packaging>pompackaging>
通过将包装设置为pom类型,我们声明该项目将作为父级或聚合器;它不会产生更多的工件。
现在,当我们的聚合器完成后,我们可以生成我们的子模块。
但是,我们需要注意,这是所有要共享的配置所在的位置,最终会在子模块中重复使用。除此之外,我们可以在这里使用dependencyManagement 或 pluginManagement 。
由于我们的父 POM 被命名为parent-project,我们需要确保我们在父目录中并运行生成命令:
cd parent-project
mvn archetype:generate -DgroupId=com.baeldung -DartifactId=core
mvn archetype:generate -DgroupId=com.baeldung -DartifactId=service
mvn archetype:generate -DgroupId=com.baeldung -DartifactId=webapp
注意使用的命令。这与我们用于父级的相同。这里的问题是,这些模块是常规的 Maven 项目,但 Maven 认识到它们是嵌套的。当我们将目录切换到parent-project时,发现 parent 有pom 类型的打包,它会相应地修改pom.xml 文件。
在parent-project的 pom.xml 中,它将在modules部分中添加所有子模块:
<modules>
<module>coremodule>
<module>servicemodule>
<module>webappmodule>
modules>
在各个子模块的pom.xml中,它将在父部分添加父项目:
<parent>
<artifactId>parent-projectartifactId>
<groupId>com.baeldunggroupId>
<version>1.0-SNAPSHOTversion>
parent>
接下来,Maven会成功生成这三个子模块。
重要的是要注意子模块只能有一个父模块。但是,我们可以导入许多 BOM。有关 BOM 文件的更多详细信息,请参阅本文。
现在我们可以一次构建所有三个模块。在父项目目录中,我们将运行:
mvn package
这将构建所有模块。我们应该看到该命令的以下输出:
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] parent-project [pom]
[INFO] core [jar]
[INFO] service [jar]
[INFO] webapp [war]
...
[INFO] Reactor Summary for parent-project 1.0-SNAPSHOT:
[INFO] parent-project ..................................... SUCCESS [ 0.272 s]
[INFO] core ............................................... SUCCESS [ 2.043 s]
[INFO] service ............................................ SUCCESS [ 0.627 s]
[INFO] webapp ............................................. SUCCESS [ 0.572 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
Reactor 列出了 parent-project,但由于它是pom类型,因此将其排除在外,并且构建会为所有其他模块生成三个单独的.jar文件。在这种情况下,构建发生在其中三个中。
此外,Maven Reactor 将分析我们的项目并以正确的顺序构建它。因此,如果我们的webapp 模块依赖于service模块,Maven 将首先构建service,然后 构建webapp。
依赖管理是一种为多模块父项目及其子项目集中依赖信息的机制。
当您有一组项目或模块继承了一个共同的父级时,您可以将有关依赖项的所有必需信息放在共同的pom.xml文件中。这将简化对子POM中工件的引用。
让我们看一个示例父级的pom.xml:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>5.3.16version>
dependency>
//...
dependencies>
dependencyManagement>
通过在 parent 中声明spring-core版本,所有依赖spring-core的子模块都可以只使用groupId和artifactId声明依赖,并且版本将被继承:
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
dependency>
//...
dependencies>
此外,您可以在父模块的pom.xml中为依赖管理提供排除项,这样子模块就不会继承特定的库:
<exclusions>
<exclusion>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
exclusion>
exclusions>
最后,如果子模块需要使用不同版本的托管依赖项,您可以在子模块的pom.xml文件中覆盖托管版本:
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>4.3.30.RELEASEversion>
dependency>
请注意,虽然子模块从其父项目继承,但父项目不一定具有聚合的任何模块。另一方面,父项目也可以聚合不继承自它的项目。
有关继承和聚合的更多信息,请参阅此文档。
我们可以更改每个子模块的封装类型。例如,让我们通过更新pom.xml文件将webapp模块的打包方式更改为WAR :
<packaging>warpackaging>
并在插件列表中添加maven-war-plugin :
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-war-pluginartifactId>
<version>3.3.2version>
<configuration>
<failOnMissingWebXml>falsefailOnMissingWebXml>
configuration>
plugin>
plugins>
build>
现在我们可以使用mvn clean install命令测试我们项目的构建。Maven 日志的输出应该类似于:
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] parent-project [pom]
[INFO] core [jar]
[INFO] service [jar]
[INFO] webapp [war]
//.............
[INFO] Reactor Summary for parent-project 1.0-SNAPSHOT:
[INFO]
[INFO] parent-project ..................................... SUCCESS [ 0.272 s]
[INFO] core ............................................... SUCCESS [ 2.043 s]
[INFO] service ............................................ SUCCESS [ 0.627 s]
[INFO] webapp ............................................. SUCCESS [ 1.047 s]
在本文中,我们讨论了使用 Maven 多模块的好处。我们还区分了常规 Maven 的父 POM 和聚合 POM。最后,我们探讨了如何设置一个简单的多模块来开始使用。
Maven 是一个很棒的工具,但它本身就很复杂。如果我们想了解更多关于 Maven 的详细信息,可以查看Sonatype Maven 参考 或Apache Maven 指南。如果我们寻求 Maven 多模块设置的高级用法,我们可以看看Spring Boot 项目如何利用它的用法。
本文中使用的所有代码示例都可以通过 Github 获得。