1. 概述
在本教程中,我们将学习如何使用 Maven 构建一个多模块项目。
首先,我们会介绍什么是多模块项目,并分析采用这种结构的优势。接着,我们将搭建一个示例项目。如果你对 Maven 还不太熟悉,建议先阅读 这篇 Maven 入门教程。
2. Maven 多模块项目
多模块项目由一个聚合 POM(Project Object Model)构建而成,该 POM 管理一组子模块。大多数情况下,聚合器位于项目的根目录下,并且其打包类型必须是 pom
。
子模块是普通的 Maven 项目,可以单独构建,也可以通过聚合器统一构建。
当我们通过聚合器 POM 构建项目时,每个打包类型不是 pom
的模块都会生成相应的归档文件(如 .jar
或 .war
)。
3. 使用多模块的优势
使用多模块的主要优势在于 减少重复配置。
举个例子,假设我们有一个应用包含前端和后端两个模块。如果我们在开发过程中修改了影响这两个模块的功能,没有专门的构建工具的话,我们就得分别构建两个组件,或者写脚本来编译代码、运行测试并展示结果。随着模块越来越多,管理起来会变得越来越困难。
而在实际开发中,项目可能还需要各种 Maven 插件来执行构建生命周期中的操作、共享依赖项与配置、引入 BOM(Bill of Materials)等。
因此,使用多模块的方式,我们可以:
✅ 通过一条命令构建整个应用的所有模块
✅ 自动处理模块间的构建顺序
✅ 集中管理依赖和插件版本
4. 父级 POM
Maven 支持继承机制:每个 pom.xml
文件都隐式地继承了一个父 POM,即所谓的 Super POM,它存在于 Maven 的安装目录中。Maven 会将 Super POM 和你的项目 POM 合并成一个有效的 Effective POM。
我们可以创建自己的 pom.xml
文件作为父项目。在这个父 POM 中,我们可以集中定义所有子模块需要共享的依赖、插件等配置信息。
除了继承机制外,Maven 还支持聚合(Aggregation)。使用聚合功能的父 POM 被称为 聚合 POM,它会在 pom.xml
中显式声明其包含的模块。
5. 子模块
子模块(或子项目)是普通的 Maven 项目,它们从父 POM 继承配置和依赖。通过继承,我们可以实现配置复用;而如果想一次性构建整个项目,则需要在父 POM 中明确声明这些子模块。
最终,这个父 POM 就同时扮演了 父项目 和 聚合器 的双重角色。
6. 构建应用
理解了 Maven 的模块结构后,我们来动手构建一个简单的示例项目。
我们的应用将包括三个模块:
- 代表核心业务逻辑的 core 模块
- 提供 REST API 的 service 模块
- 包含用户界面资源的 webapp 模块
为了专注于 Maven 配置,各模块的具体实现不会详细展开。
6.1. 生成父级 POM
第一步,创建父项目:
mvn archetype:generate -DgroupId=com.baeldung -DartifactId=parent-project
生成父项目后,打开 parent-project/pom.xml
文件,添加如下内容以指定打包方式为 pom
:
<packaging>pom</packaging>
设置为 pom
类型意味着该项目仅用于聚合或继承,不会生成任何构建产物。
此时聚合器已就绪,接下来可以生成子模块。
💡 注意:这是放置共享配置的地方,后续子模块会复用这里的 dependencyManagement
或 pluginManagement
等配置。
6.2. 创建子模块
由于父项目名为 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 会识别当前目录下的父 POM 并自动完成模块关联。
在 parent-project/pom.xml
中,Maven 会添加以下内容:
<modules>
<module>core</module>
<module>service</module>
<module>webapp</module>
</modules>
而在每个子模块的 pom.xml
中,会自动加上父项目的引用:
<parent>
<artifactId>parent-project</artifactId>
<groupId>com.baeldung</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
⚠️ 每个子模块只能有一个父项目,但可以通过 <dependencyManagement>
引入多个 BOM 文件。
更多关于 BOM 的内容,请参考 这篇文章。
6.3. 构建项目
现在可以一次性构建所有模块。在父项目目录下执行:
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] ------------------------------------------------------------------------
Maven Reactor 会分析模块之间的依赖关系,并按正确的顺序进行构建。例如,如果 webapp
依赖于 service
,那么 Maven 会先构建 service
再构建 webapp
。
6.4. 在父项目中启用依赖管理
依赖管理是一种集中管理多模块项目及其子模块依赖信息的机制。
当多个项目继承同一个父项目时,可以在父 POM 中统一声明依赖版本号,简化子模块中的依赖配置。
示例父 POM 的 dependencyManagement
配置:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>6.1.13</version>
</dependency>
//...
</dependencies>
</dependencyManagement>
子模块只需声明 groupId
和 artifactId
即可继承版本:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
//...
</dependencies>
还可以在父 POM 中排除某些依赖,防止被子模块继承:
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
</exclusions>
如果某个子模块需要不同的版本,也可以在子模块中覆盖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.16</version>
</dependency>
📌 注意:虽然子模块继承父项目,但父项目不一定聚合所有子模块;反之,父项目也可以聚合不继承它的项目。
更多细节请参考 官方文档。
6.5. 更新子模块并重新构建
我们可以更改每个子模块的打包类型。例如,将 webapp
模块的打包类型改为 war
,并添加 maven-war-plugin
插件:
<packaging>war</packaging>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.4.0</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
再次运行 mvn clean install
,输出应类似如下:
[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]
7. 总结
本文介绍了 Maven 多模块项目的优点,区分了普通父 POM 与聚合 POM 的区别,并演示了如何搭建一个多模块项目的基础结构。
Maven 是一个强大的工具,但也相对复杂。如需深入学习,可查阅 Sonatype Maven 参考手册 或 Apache Maven 官方指南。若想了解高级用法,可以研究 Spring Boot 项目是如何使用多模块结构的。