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

    • 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章 多模块项目深入

    • Reactor构建顺序
    • 继承与聚合组合实践
    • 模块间依赖
  • 第2章 插件体系深入

    • 插件目标goal
    • execution与自定义绑定
    • pluginManagement
  • 第3章 Profile高级应用

    • Profile激活机制
    • Profile与Spring Profile区别
  • 第4章 部署与分发

    • distributionManagement
    • mvn deploy
    • settings.xml认证配置
  • 第5章 CI/CD集成

    • Maven与持续集成
  • 第6章 自定义插件开发

    • 自定义插件开发
  • 第7章 高级依赖管理

    • 快照版本机制
    • 依赖分析工具
  • 第8章 仓库管理深入

    • 仓库组与路由

Reactor构建顺序

本章承接入门教程中"聚合的基础概念",深入讲解聚合之后 Maven 如何决定多模块的构建顺序。理解 Reactor 的排序机制,是排查"为什么模块 A 在模块 B 之前编译"、"为什么修改了模块却未生效"等问题的关键。


核心机制

当你在一个聚合项目的根目录执行 mvn clean install 时,Maven 并不会简单地按照 pom.xml 中 <modules> 标签的声明顺序逐个构建。它会先启动一个名为 Reactor(反应堆)的内部引擎,对整个多模块项目的依赖关系图进行一次拓扑排序,然后按照计算出的顺序执行构建。

Reactor 的官方定位是 Maven 的项目构建编排器。它的职责不是编译代码,而是读懂模块之间的依赖关系,生成一个不会导致"依赖缺失"的构建序列。换句话说,Reactor 确保:如果模块 B 依赖模块 A 的产物,那么模块 A 一定在模块 B 之前被构建和安装到本地仓库。

构建顺序的决定因素

Reactor 的排序逻辑由两个因素共同决定,依赖关系优先于声明顺序:

排序因素优先级说明
模块间依赖关系高如果模块 B 依赖模块 A,A 必须在 B 之前构建
<modules> 声明顺序低无依赖关系的模块之间,按声明顺序排列

这意味着:即使你在父 POM 里先写了 <module>web-ui</module>、后写了 <module>core-api</module>,只要 web-ui 依赖 core-api,Reactor 也会强制把 core-api 排在前面。

Reactor Build Order 输出解读

执行多模块构建时,Maven 会在日志开头打印 Reactor 计算出的构建顺序:

[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] ------------------------------------------------------------------------
[INFO] feixiang-parent                                                      [pom]
[INFO] feixiang-core-api                                                    [jar]
[INFO] feixiang-service                                                     [jar]
[INFO] feixiang-web-ui                                                      [war]
[INFO] ------------------------------------------------------------------------

这五行输出的含义:

  1. feixiang-parent:聚合根 POM,本身没有代码,但 Reactor 会把它纳入序列(用于统一版本和插件管理)
  2. feixiang-core-api:被其他模块依赖的基础模块,Reactor 通过依赖分析将其前置
  3. feixiang-service:业务服务层,依赖 core-api
  4. feixiang-web-ui:前端 Web 层,依赖 service 和 core-api,被排在最后

关键认知:Reactor Build Order 不是"建议顺序",而是强制顺序。Maven 会严格按照这个列表从上到下执行 clean、compile、test、package、install 等生命周期阶段。

生活类比:餐厅后厨的出菜顺序

想象飞翔科技楼下有一家大型餐厅,后厨同时准备一桌宴席的 4 道菜:

  • 汤(core-api):需要提前 2 小时慢炖,其他菜都要用它做汤底
  • 主菜(service):需要汤底才能烹饪
  • 配菜(web-ui):需要主菜和汤底一起摆盘
  • 菜单(parent):本身不是菜,但规定了所有菜的调味标准

即使服务员给后厨的订单上先写了"配菜"、后写了"汤",主厨(Reactor)也会重新排单:汤 → 主菜 → 配菜。菜单虽然不烹饪,但主厨会先看一眼菜单确认标准。这就是 Reactor 的逻辑——依赖关系决定物理顺序,声明顺序只在无依赖时生效。


图示

上图展示了 Reactor 排序的核心逻辑:左侧是 pom.xml 中 <modules> 的声明顺序(web-ui 在前),右侧是 Reactor 实际计算出的构建顺序(core-api 在前)。下方的依赖关系图揭示了排序反转的原因——依赖关系压倒了声明顺序。Reactor 读取所有子模块的 POM,解析它们之间的依赖边,然后执行拓扑排序,确保没有任何模块在其依赖项之前被构建。


完整示例

场景

飞翔科技正在开发一套电商中台系统。CTO 大翔要求项目拆分为 4 个模块:

  • feixiang-parent:统一版本管理(packaging = pom)
  • feixiang-core-api:公共 DTO 和接口(被所有模块依赖)
  • feixiang-service:业务逻辑层(依赖 core-api)
  • feixiang-web-ui:Web 控制器和前端资源(依赖 service 和 core-api)

架构师白歌在 parent 的 pom.xml 中按"从上层到下层"的思路声明了模块顺序:

操作前的配置

feixiang-parent/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-parent</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <modules>
        <module>feixiang-web-ui</module>
        <module>feixiang-service</module>
        <module>feixiang-core-api</module>
    </modules>
</project>

注意:白歌把 feixiang-web-ui 放在了第一个,因为他觉得"Web 层是入口,应该优先"。

feixiang-web-ui/pom.xml 中的依赖声明:

<dependencies>
    <dependency>
        <groupId>com.feixiang</groupId>
        <artifactId>feixiang-service</artifactId>
        <version>${project.version}</version>
    </dependency>
    <dependency>
        <groupId>com.feixiang</groupId>
        <artifactId>feixiang-core-api</artifactId>
        <version>${project.version}</version>
    </dependency>
</dependencies>

feixiang-service/pom.xml 中的依赖声明:

<dependencies>
    <dependency>
        <groupId>com.feixiang</groupId>
        <artifactId>feixiang-core-api</artifactId>
        <version>${project.version}</version>
    </dependency>
</dependencies>

操作步骤

后端工程师小崔在 feixiang-parent 目录下执行:

mvn clean install

操作结果

Maven 输出的 Reactor Build Order 如下:

[INFO] --------------------< com.feixiang:feixiang-parent >--------------------
[INFO] Building feixiang-parent 1.0.0-SNAPSHOT                              [1/4]
[INFO] --------------------------------[ pom ]---------------------------------
...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] ------------------------------------------------------------------------
[INFO] feixiang-parent                                                      [pom]
[INFO] feixiang-core-api                                                    [jar]
[INFO] feixiang-service                                                     [jar]
[INFO] feixiang-web-ui                                                      [war]
[INFO] ------------------------------------------------------------------------
...
[INFO] -----------------< com.feixiang:feixiang-core-api >-------------------
[INFO] Building feixiang-core-api 1.0.0-SNAPSHOT                            [2/4]
...
[INFO] -----------------< com.feixiang:feixiang-service >--------------------
[INFO] Building feixiang-service 1.0.0-SNAPSHOT                             [3/4]
...
[INFO] ------------------< com.feixiang:feixiang-web-ui >--------------------
[INFO] Building feixiang-web-ui 1.0.0-SNAPSHOT                              [4/4]

变化分析:

  • 尽管 pom.xml 中 <modules> 的顺序是 web-ui → service → core-api,Reactor 实际构建顺序却是 parent → core-api → service → web-ui
  • core-api 被强制前置,因为 service 和 web-ui 都声明了对它的依赖
  • service 排在 web-ui 之前,因为 web-ui 依赖 service
  • 如果小崔在 core-api 中修改了一个 DTO 类,重新执行 mvn clean install 后,service 和 web-ui 都会自动基于最新版本重新编译

易错点与常见问题

误区一:Reactor 顺序就是 <modules> 的声明顺序

错误认知:"我在父 POM 里把模块 A 写在模块 B 前面,所以 A 一定先构建。"

纠正:<modules> 的声明顺序只在模块之间没有依赖关系时才生效。一旦存在依赖关系,Reactor 会完全忽略声明顺序,按照拓扑排序结果执行。如果你把 web-ui 写在第一个,但 web-ui 依赖 core-api,Reactor 依然会把 core-api 排在前面。

误区二:Reactor 会并行构建无依赖关系的模块

错误认知:"core-api 和 parent 没有代码依赖,它们应该可以并行编译。"

纠正:默认情况下,Maven 的 Reactor 是串行执行的。即使两个模块之间没有依赖关系,Maven 也会按顺序逐个构建。如果你需要并行构建,必须显式启用 -T 参数(如 mvn -T 4 clean install),且并行度受限于模块间的依赖边。Reactor 的拓扑排序结果仍然是并行调度的前提——没有依赖关系的模块才可能被并行,有依赖关系的模块绝对不能并行。

误区三:修改了父 POM 版本,子模块会自动使用新版本

错误认知:"我把 parent 的版本从 1.0.0-SNAPSHOT 改成了 1.0.0,所有子模块应该自动跟着变。"

纠正:Reactor 负责排序和调度,不负责版本传播。子模块的 <parent> 标签中显式声明了 <version>,这个版本必须手动更新(或通过 CI/CD 脚本批量替换)。Reactor 在构建时如果发现子模块引用的 parent 版本与当前目录中的 parent POM 版本不一致,会先去本地仓库或远程仓库查找对应版本的 parent,而不是直接使用当前目录下的 parent 文件。这是多模块构建中最常见的"修改了 parent 却不生效"问题的根源。


小结

Reactor 是 Maven 多模块构建的隐形导演。它不显式配置,却决定了所有模块的出场顺序。其核心规则是:依赖关系优先于声明顺序。开发者应当学会阅读 Reactor Build Order 输出,将其作为验证模块依赖关系是否正确的工具——如果某个模块的位置和你预期不符,往往意味着它的 pom.xml 中多声明或少声明了一条依赖。

本章与全局的关系:本章回答了"聚合后模块按什么顺序构建"。下一章"模块间依赖"将深入讲解子模块之间如何通过 GAV 坐标相互引用,以及循环依赖的检测与规避。

下一页
继承与聚合组合实践