源本科技 | 码上会

Maven 聚合与继承

2026/03/17
3
0

引言

在云原生和微服务架构下,一个大型系统往往被拆分为数十甚至上百个模块。如果每个模块都独立配置依赖、插件和版本,维护成本将呈指数级上升。Maven 提供了两大核心机制来解决这一问题:聚合(Aggregation)与继承(Inheritance)

  • 聚合是项目的“骨架”,负责将分散的模块组织在一起,实现一键构建。

  • 继承是项目的“灵魂”,负责统一管理配置和依赖版本,消除重复劳动。

理解并熟练运用这两者,是构建标准化、可维护的大型 Maven 项目的基础。


聚合工程

什么是聚合

聚合是指在一个父工程中,通过声明的方式将多个子模块(Modules)组合在一起。

  • 核心作用:允许开发者在根目录下执行一次 Maven 命令(如 mvn clean install),Maven 就会自动按照正确的顺序编译、测试、打包所有子模块。

  • 物理形态:聚合工程本身通常不包含任何 Java 源代码,它的 pom.xml 仅作为管理入口存在。

如何创建聚合工程

创建一个聚合工程非常简单,关键在于两个配置点:

设置打包类型

父工程的 <packaging> 必须设置为 pom。这告诉 Maven:“我是一个容器,不生成 JAR 或 WAR 包”。

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example.cloud</groupId>
    <artifactId>cloud-parent</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    
    <!-- 关键点:打包类型必须为 pom -->
    <packaging>pom</packaging>
    
    <name>Cloud Native Parent</name>
</project>

声明子模块列表

使用 <modules> 标签列出所有子模块的目录名(相对路径)。

<modules>
    <!-- 对应目录:cloud-common -->
    <module>cloud-common</module>
    
    <!-- 对应目录:cloud-core -->
    <module>cloud-core</module>
    
    <!-- 对应目录:cloud-admin -->
    <module>cloud-admin</module>
</modules>

智能构建顺序

Maven 的聚合机制不仅仅是简单的循环执行。它会自动分析模块间的依赖关系,构建一个有向无环图,从而确定最佳构建顺序。

  • 场景:如果 cloud-admin 依赖 cloud-core,而 cloud-core 依赖 cloud-common

  • 结果:即使你在 <modules> 中把 cloud-admin 写在第一个,Maven 也会自动先构建 cloud-common,然后是 cloud-core,最后才是 cloud-admin

  • 优势:开发者无需手动排序,彻底解放了构建流程的管理负担。

注意:聚合关系是单向感知的。父工程知道有哪些子模块,但子模块的 pom.xml 中并不需要(也不应该)声明自己属于哪个聚合工程。子模块只需关注自身的逻辑。


继承关系

为什么要继承

随着模块数量增加,以下问题会日益凸显:

  • 配置冗余:每个模块都要重复配置相同的编译器插件、资源过滤规则等。

  • 版本地狱:每个模块都单独声明 Spring、MyBatis 等依赖的版本。一旦需要升级版本,必须修改几十个文件,极易遗漏导致版本冲突。

继承机制允许子模块从父模块“继承”配置,实现“一处定义,处处生效”。

如何建立继承

继承关系的建立需要父子双方配合,但主动权在子模块

父工程配置

父工程除了设置 <packaging>pom</packaging> 外,主要提供两类内容:

  1. 直接依赖 (<dependencies>):所有子模块都需要的公共库(如 lombok, slf4j)。子模块继承后自动拥有这些依赖。

  2. 依赖管理 (<dependencyManagement>)这是最核心的用法。在此处定义依赖的版本号,但不引入实际依赖。子模块如需使用,只需声明 groupIdartifactId,无需写版本号。

<!-- 父工程 pom.xml -->
<project>
    <!-- ... 基础坐标信息 ... -->
    <packaging>pom</packaging>

    <!-- 1. 统一属性管理 (推荐) -->
    <properties>
        <java.version>17</java.version>
        <spring.boot.version>3.1.0</spring.boot.version>
        <mysql.version>8.0.33</mysql.version>
    </properties>

    <!-- 2. 公共依赖 (所有子模块自动继承) -->
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <!-- 3. 依赖版本锁定 (子模块按需引用,不写版本) -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>${spring.boot.version}</version>
            </dependency>
            <dependency>
                <groupId>com.mysql</groupId>
                <artifactId>mysql-connector-j</artifactId>
                <version>${mysql.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
    <!-- 还可以继承插件配置、仓库配置等 -->
</project>

子工程配置

子模块通过 <parent> 标签显式指向父工程。

<!-- 子模块 pom.xml -->
<project>
    <!-- 关键点:声明父工程 -->
    <parent>
        <groupId>com.example.cloud</groupId>
        <artifactId>cloud-parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <!-- 相对路径,默认为 ../pom.xml,若目录结构特殊需显式指定 -->
        <relativePath>../pom.xml</relativePath>
    </parent>

    <!-- 子模块只需定义自己的 artifactId,groupId 和 version 默认继承父工程 -->
    <artifactId>cloud-admin</artifactId>
    <packaging>jar</packaging>

    <!-- 使用依赖时,无需写版本号! -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <!-- 版本由父工程 dependencyManagement 控制 -->
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <!-- 版本由父工程 control -->
        </dependency>
    </dependencies>
</project>

版本控制

除了 <dependencyManagement>,强烈建议使用 <properties> 来管理版本号变量。

  • 好处

    • 语义化清晰:${spring.boot.version} 比硬编码 3.1.0 更易读。

    • 覆盖灵活:子模块如果需要特殊版本,可以在自己的 <properties> 中覆盖父工程的变量值,而无需修改父工程。