乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 介绍与核心概念

    • Maven是什么
    • 约定优于配置
  • 第2章 安装与配置

    • 安装与验证
    • settings.xml
    • 本地仓库与镜像
  • 第3章 POM与项目坐标

    • POM
    • GAV坐标
    • packaging
  • 第4章 标准目录布局

    • 标准目录布局
  • 第5章 依赖机制

    • dependencies
    • scope
    • 依赖传递
    • 依赖冲突与调解
    • exclusions
    • optional
    • dependencyManagement
  • 第6章 仓库

    • 仓库体系
    • 本地仓库
    • 远程仓库与镜像
    • 私服
  • 第7章 构建生命周期

    • 生命周期概述
    • clean 生命周期
    • default 生命周期
    • site 生命周期
    • 生命周期与插件绑定
  • 第8章 插件

    • 插件概述
    • maven-compiler-plugin
    • maven-surefire-plugin
    • maven-war-plugin
  • 第9章 继承与聚合

    • parent继承
    • 聚合
    • BOM
    • properties
  • 第10章 属性与资源过滤

    • 资源过滤
    • Profile
  • 第11章 常用命令

    • mvn compile
    • mvn test
    • mvn package
    • mvn clean
    • mvn install
    • mvn dependency:tree
  • 第12章 常见问题与最佳实践

    • 依赖冲突排查
    • 最佳实践

聚合

本章承接 parent 继承,讲解 Maven 多模块项目的另一种组织方式:聚合(Aggregation)。如果说继承是"集团总部给分店发手册",聚合就是"集团总部统一调度所有分店同时营业"。在实际项目中,继承与聚合通常配合使用,但理解它们的独立概念是正确使用的前提。


核心机制

聚合项目本身不生产代码,它的唯一职责是统一管理一组模块的构建顺序和构建行为。当你在一个聚合项目的根目录执行 mvn clean install,Maven 会自动按依赖顺序构建所有子模块。

聚合 POM 通过 <modules> 标签声明它管理的子模块:

关键规则:

  • 聚合 POM 的 <packaging> 必须是 pom
  • 每个 <module> 的值是子模块目录的相对路径(通常是目录名)
  • Maven 会进入每个模块目录,找到其中的 pom.xml 并执行构建
  • 构建顺序由模块间的依赖关系自动计算,而非 <modules> 的声明顺序

聚合与继承的区别

这是最容易混淆的两个概念,必须清晰区分:

维度继承(Inheritance)聚合(Aggregation)
关系方向子 POM 指向父 POM(<parent>)父 POM 指向子模块(<modules>)
核心目的共享配置(版本、插件、属性)统一构建(一次命令构建多个模块)
packaging父 POM 必须是 pom聚合 POM 必须是 pom
是否必须同时用否否
典型场景统一依赖版本一键构建微服务全家桶

关键结论:继承解决"配置复用"问题,聚合解决"构建协调"问题。两者可以独立存在,也可以在一个 POM 中同时存在(最常见)。

多模块项目的典型结构

feixiang-platform/          # 聚合 + 继承 POM(根目录)
├── pom.xml                 # 定义 modules、parent、共享配置
├── employee-system/        # 子模块1
│   └── pom.xml
├── payroll-service/        # 子模块2
│   └── pom.xml
└── employee-web/           # 子模块3
    └── pom.xml

在这个结构中,根 pom.xml 同时扮演三个角色:

  1. 聚合器:通过 <modules> 管理三个子模块
  2. 父 POM:通过 <parent> 被三个子模块继承(如果子模块指向它)
  3. 构建入口:开发者只在根目录执行 Maven 命令

生活类比:交响乐团指挥

想象飞翔科技的技术团队是一支交响乐团:

  • 聚合 POM = 指挥家。指挥家自己不拉小提琴、不吹长笛,他的职责是举起指挥棒,让所有乐手(子模块)在同一时刻开始演奏。
  • 继承 = 乐谱规范。所有小提琴手用的乐谱格式相同(继承父 POM 的配置),但每个声部演奏的音符不同(子模块自己的依赖)。
  • mvn clean install = 演出开始。指挥棒一挥,弦乐组(employee-system)、木管组(payroll-service)、铜管组(employee-web)按预定顺序依次进入,最终合成一首完整的交响曲(可部署的系统)。

如果没有指挥家(聚合),你需要分别走到每个乐手面前说"请开始"——这就是没有聚合时,逐个进入子目录执行 mvn install 的痛苦。


图示

上图展示了聚合与继承的配合关系:根 POM 通过 <modules> 聚合三个子模块(实线),同时通过 parent 机制被三个子模块继承配置(虚线)。开发者只需在根目录执行一次 Maven 命令,所有模块按依赖顺序自动构建。


完整示例

场景

飞翔科技有三个服务:员工系统 employee-system、薪资服务 payroll-service、Web 门户 employee-web。其中 employee-web 依赖 employee-system 的 API 模块。CTO 大翔要求:一键构建所有服务,且构建顺序必须正确(被依赖的先构建)。

操作前:各自为政

没有聚合时,小崔需要逐个目录执行构建:

$ cd employee-system && mvn clean install
$ cd ../payroll-service && mvn clean install
$ cd ../employee-web && mvn clean install

问题:

  • 如果 employee-web 依赖 employee-system,但小崔忘了先构建后者,employee-web 会报依赖找不到
  • 黄俪想一次性构建整个平台,不得不写复杂的 shell 脚本
  • 李眉的 Jenkins 流水线需要配置多个步骤,维护成本高

操作步骤:创建聚合项目

第一步:在根目录创建 feixiang-platform/pom.xml

<?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>com.feixiang</groupId>
    <artifactId>feixiang-platform</artifactId>
    <version>2.0.0</version>
    <packaging>pom</packaging>

    <name>飞翔科技技术平台</name>
    <description>统一聚合所有业务模块</description>

    <modules>
        <module>employee-system</module>
        <module>payroll-service</module>
        <module>employee-web</module>
    </modules>

    <!-- 同时作为父 POM,提供共享配置 -->
    <properties>
        <java.version>17</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.feixiang</groupId>
                <artifactId>employee-system</artifactId>
                <version>${project.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

第二步:确保子模块的 pom.xml 指向父 POM

以 employee-web/pom.xml 为例:

<project>
    <parent>
        <groupId>com.feixiang</groupId>
        <artifactId>feixiang-platform</artifactId>
        <version>2.0.0</version>
    </parent>

    <artifactId>employee-web</artifactId>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>com.feixiang</groupId>
            <artifactId>employee-system</artifactId>
            <!-- 版本由父 POM 的 dependencyManagement 管理 -->
        </dependency>
    </dependencies>
</project>

操作结果:一键构建

在根目录执行:

$ cd feixiang-platform
$ mvn clean install
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] 
[INFO] feixiang-platform                                    [pom]
[INFO] employee-system                                      [jar]
[INFO] payroll-service                                      [jar]
[INFO] employee-web                                         [war]
[INFO] 
[INFO] --- maven-clean-plugin:3.2.0:clean (default-clean) @ feixiang-platform ---
...
[INFO] --- maven-install-plugin:3.1.1:install (default-install) @ employee-web ---
[INFO] Installing target\employee-web-2.0.0.war to ...\repository\com\feixiang\employee-web\2.0.0\employee-web-2.0.0.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

Reactor Build Order 分析:

  • Maven 自动计算了构建顺序:feixiang-platform(根)→ employee-system → payroll-service → employee-web
  • 虽然 <modules> 中 employee-web 写在第三位,但 Maven 检测到它依赖 employee-system,确保被依赖模块先构建
  • 小崔只需执行一条命令,四个模块全部构建并安装到本地仓库
  • 李眉的 Jenkins 流水线只需配置一个步骤:mvn clean install

易错点与常见问题

误区一:聚合 POM 的 packaging 不是 pom

错误配置:

<artifactId>feixiang-platform</artifactId>
<packaging>jar</packaging>  <!-- 错误! -->

后果:Maven 尝试为聚合项目编译源代码、生成 JAR,但聚合项目通常没有 src/main/java。构建会失败或产生无意义的空 JAR。聚合 POM 和父 POM 一样,packaging 必须是 pom。

误区二:module 路径写错

错误配置:

<modules>
    <module>./employee-system</module>  <!-- 多余的前缀 ./ -->
</modules>

或

<modules>
    <module>employee-system/pom.xml</module>  <!-- 不应该写 pom.xml -->
</modules>

纠正:<module> 的值应该是子模块目录的相对路径,默认就是目录名。不要加 ./、../(除非确实在父目录之外),也不要加 /pom.xml。Maven 会自动在指定目录下寻找 pom.xml。

误区三:聚合和继承混为一谈,认为用了聚合就自动继承

错误认知:"我在根 POM 里写了 <modules>,子模块就会自动继承根 POM 的配置。"

纠正:不会。聚合和继承是两个独立的机制。根 POM 写 <modules> 只是告诉 Maven"我要构建这些子模块";子模块是否继承根 POM 的配置,取决于子模块自己的 <parent> 标签是否指向根 POM。

常见做法(推荐):让根 POM 同时扮演聚合器和父 POM 两个角色,即根 POM 写 <modules>,子 POM 写 <parent> 指向根 POM。这样既能一键构建,又能共享配置。


小结

聚合(Aggregation)是 Maven 管理多模块项目的构建协调机制。聚合 POM 通过 <modules> 声明管理的子模块,通过 packaging=pom 表明自己不产生代码。继承解决"配置复用",聚合解决"构建协调",两者通常配合使用。在聚合根目录执行一次 Maven 命令,所有模块按依赖关系自动排序构建,极大提升了多服务项目的开发效率。

本章与全局的关系:本章讲解了多模块项目的横向组织方式。下一节将深入讲解 properties 标签,它是父 POM 统一管理和子 POM 灵活引用的核心工具。

上一页
parent继承
下一页
BOM