《玩转Maven多模块项目:继承、聚合与项目结构设计》

kayokoi 发布于 2025-07-27 54 次阅读


随着项目规模的扩大和复杂性的增加,将一个庞大的单体应用拆分成多个更小、更专注的模块成为一种常见的做法。Maven对多模块项目(Multi-Module Project)提供了优秀的支持,使得模块化的设计、构建和管理变得更为便捷。本文将详细探讨如何使用Maven构建多模块项目,重点理解父POM的继承机制和聚合POM的统一构建能力,以及项目结构设计的考量。

为什么需要多模块项目?

多模块项目,也称为分模块设计,是将一个大型项目按照功能或逻辑结构拆分成若干个独立的子模块。这样做的好处显而易见:

  • 代码复用:公共功能(如工具类、核心业务逻辑)可以抽取到单独模块,被其他模块依赖和复用。
  • 关注点分离:每个模块专注于特定功能,降低了模块间的耦合度,使得开发和维护更为清晰。
  • 并行开发:不同团队或开发者可以并行开发不同模块,提高开发效率。
  • 灵活构建与部署:可以单独构建或部署某个模块,而不必每次都构建整个项目。
  • 更易管理:每个模块都有自己的 pom.xml,职责更明确。

实际开发中,通常是先针对整体功能进行模块化设计,再进行编码,而不是项目开发完毕后再进行拆分。

Maven多模块项目的核心:父POM与聚合POM

在Maven中,多模块项目的管理主要依赖于两种具有 <packaging>pom</packaging> 的特殊POM:

  1. 父POM (Parent POM):利用Maven的继承特性。
  2. 聚合POM (Aggregator POM):利用Maven的聚合特性。

通常,在一个多模块项目中,根目录下的POM文件会同时扮演父POM和聚合POM的角色

1. 父POM:通过 <parent> 实现配置继承

子模块通过在其 pom.xml 文件中使用 <parent> 标签来指定其父POM。一旦指定,子模块将继承父POM中的诸多配置,包括:

  • groupIdversion (如果子模块未显式定义,则默认继承父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)**机制会启动:

  1. 它会解析聚合POM,找到 <modules> 中列出的所有子模块。
  2. 分析这些模块之间的依赖关系。
  3. 按照正确的顺序构建所有模块(例如,如果 my-app-web 依赖 my-app-service,则 my-app-service 会先被构建)。
  4. 这实现了项目的“一键构建”,极大地简化了多模块项目的构建操作。

聚合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>

聚合工程可以明确感知到参与聚合的模块有哪些。

项目结构设计

在实际项目中,通常有两种常见的目录结构:

  1. 分层结构 (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> 指向是正确的。

  2. 扁平结构 (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构建生命周期与插件核心:定制化你的项目构建流程》