随着项目规模的扩大和复杂性的增加,将一个庞大的单体应用拆分成多个更小、更专注的模块成为一种常见的做法。Maven对多模块项目(Multi-Module Project)提供了优秀的支持,使得模块化的设计、构建和管理变得更为便捷。本文将详细探讨如何使用Maven构建多模块项目,重点理解父POM的继承机制和聚合POM的统一构建能力,以及项目结构设计的考量。
为什么需要多模块项目?
多模块项目,也称为分模块设计,是将一个大型项目按照功能或逻辑结构拆分成若干个独立的子模块。这样做的好处显而易见:
- 代码复用:公共功能(如工具类、核心业务逻辑)可以抽取到单独模块,被其他模块依赖和复用。
- 关注点分离:每个模块专注于特定功能,降低了模块间的耦合度,使得开发和维护更为清晰。
- 并行开发:不同团队或开发者可以并行开发不同模块,提高开发效率。
- 灵活构建与部署:可以单独构建或部署某个模块,而不必每次都构建整个项目。
- 更易管理:每个模块都有自己的
pom.xml
,职责更明确。
实际开发中,通常是先针对整体功能进行模块化设计,再进行编码,而不是项目开发完毕后再进行拆分。
Maven多模块项目的核心:父POM与聚合POM
在Maven中,多模块项目的管理主要依赖于两种具有 <packaging>pom</packaging>
的特殊POM:
- 父POM (Parent POM):利用Maven的继承特性。
- 聚合POM (Aggregator POM):利用Maven的聚合特性。
通常,在一个多模块项目中,根目录下的POM文件会同时扮演父POM和聚合POM的角色。
1. 父POM:通过 <parent>
实现配置继承
子模块通过在其 pom.xml
文件中使用 <parent>
标签来指定其父POM。一旦指定,子模块将继承父POM中的诸多配置,包括:
groupId
和version
(如果子模块未显式定义,则默认继承父POM的)。<properties>
:共享属性。<dependencyManagement>
:统一的依赖版本管理。<pluginManagement>
:统一的插件版本和默认配置管理。<repositories>
和<pluginRepositories>
:仓库配置。<distributionManagement>
:部署配置。
父POM (project-root/pom.xml
) 示例:
```xml <project ...> <modelVersion>4.0.0</modelVersion> <groupId>com.example.myapp</groupId> <artifactId>my-app-parent</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>pom</packaging> <properties> <java.version>11</java.version> <spring.boot.version>2.7.0</spring.boot.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.example.myapp</groupId>
<artifactId>my-app-core-utils</artifactId>
<version>${project.version}</version> </dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<modules>
<module>my-app-core-utils</module>
<module>my-app-web</module>
<module>my-app-service</module>
</modules>
</project>
**子模块 (`project-root/my-app-web/pom.xml`) 示例:**
```xml
<project ...>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example.myapp</groupId>
<artifactId>my-app-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> </parent>
<artifactId>my-app-web</artifactId> <packaging>jar</packaging> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.example.myapp</groupId>
<artifactId>my-app-core-utils</artifactId>
</dependency>
<dependency>
<groupId>com.example.myapp</groupId>
<artifactId>my-app-service</artifactId>
<version>${project.version}</version> </dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
注意: 父POM无法直接感知哪些子模块继承了自己。继承是子模块单向声明的。
2. 聚合POM:通过 <modules>
实现统一构建
聚合POM(通常也是父POM)使用 <modules>
标签列出所有它管理的子模块的目录名。当你在聚合POM的目录下执行Maven命令(如 mvn clean install
)时,Maven的**反应堆 (Reactor)**机制会启动:
- 它会解析聚合POM,找到
<modules>
中列出的所有子模块。 - 分析这些模块之间的依赖关系。
- 按照正确的顺序构建所有模块(例如,如果
my-app-web
依赖my-app-service
,则my-app-service
会先被构建)。 - 这实现了项目的“一键构建”,极大地简化了多模块项目的构建操作。
聚合POM (project-root/pom.xml
) 中的 <modules>
部分:
<project ...>
...
<packaging>pom</packaging> ...
<modules>
<module>my-app-core-utils</module> <module>my-app-service</module> <module>my-app-web</module> </modules>
...
</project>
聚合工程可以明确感知到参与聚合的模块有哪些。
项目结构设计
在实际项目中,通常有两种常见的目录结构:
-
分层结构 (Hierarchical Structure): 父工程(聚合POM和父POM)位于根目录,所有子模块作为其子目录。这是最常见和推荐的结构,层级清晰。
my-app/ ├── pom.xml (父POM & 聚合POM) ├── my-app-core-utils/ │ └── pom.xml ├── my-app-service/ │ └── pom.xml └── my-app-web/ └── pom.xml
在这种结构下,子模块
pom.xml
中的<relativePath>../pom.xml</relativePath>
指向是正确的。 -
扁平结构 (Flat Structure): 父工程和所有子模块位于同一级目录。这种结构也能工作,但层级关系不如分层结构直观。
projects/ ├── my-app-parent/ (父POM) │ └── pom.xml ├── my-app-aggregator/ (聚合POM,也可能同时是父POM) │ └── pom.xml ├── my-app-core-utils/ │ └── pom.xml ├── my-app-service/ │ └── pom.xml └── my-app-web/ └── pom.xml
在这种情况下,
<relativePath>
的设置需要更小心,或者如果父POM已安装到本地仓库,可以省略<relativePath>
,Maven会从仓库查找。但为了构建的独立性和可移植性,推荐使用明确的相对路径。
大多数项目,如 yudao-cloud
,都采用分层结构,根POM既是父POM也是聚合POM。
模块间依赖
在一个多模块项目中,模块之间通常存在依赖关系。例如,my-app-web
模块可能依赖于 my-app-service
模块提供的服务,而 my-app-service
模块可能依赖于 my-app-core-utils
模块提供的工具类。
这种依赖关系在子模块的 pom.xml
中通过常规的 <dependencies>
声明:
<dependencies>
<dependency>
<groupId>com.example.myapp</groupId> <artifactId>my-app-core-utils</artifactId>
<version>${project.version}</version> </dependency>
</dependencies>
Maven的反应堆机制会确保在构建 my-app-service
之前,my-app-core-utils
已经被构建和安装(至少到本地仓库)。
系列文章:
- 《Maven入门:剖析POM核心与GAVP项目坐标》
- 《Maven依赖管理精解:从
<dependencies>
到<dependencyManagement>
与BOM实践》 - 《Maven
<packaging>pom</packaging>
深度解析:父POM、聚合POM与BOM的三重身份》 - 当前: 《玩转Maven多模块项目:继承、聚合与项目结构设计》
- 下一篇: 《掌控Maven构建生命周期与插件核心:定制化你的项目构建流程》
Comments NOTHING