第 8 章 · 多项目构建 & Composite Build

4 个互动演示,搞懂模块拆分、模块依赖图、includeBuild、常用命令。

为什么大型项目要拆模块?

用一个真实电商系统的例子:用户、商品、订单、支付——把它们都塞进一个工程,会怎么样?

❌ 单模块(所有代码塞一起)

my-shop/ ├── build.gradle.kts // 1 个超大脚本 └── src/main/java/com/shop/ ├── user/ // 用户模块 ├── product/ // 商品模块 ├── order/ // 订单模块 ├── payment/ // 支付模块 └── infra/ // 基础设施
改 1 行
编译全工程
代码边界
无法约束
协作冲突
天天 merge

✅ 多模块(按业务/层次拆)

my-shop/ ├── build-logic/ // 共享构建逻辑 ├── settings.gradle.kts // 拼装清单 ├── app/ // 启动模块 ├── feature-user/ // 用户功能 ├── feature-product/ // 商品功能 ├── feature-order/ // 订单功能 ├── feature-payment/ // 支付功能 ├── lib-core/ // 公共领域模型 └── lib-infra/ // 数据库/缓存
改 1 行
仅编译相关模块
代码边界
由依赖配置约束
协作冲突
小组各管一摊

💡 真实生活类比:搬家

单模块 = 把所有衣服、书、餐具堆在一个大箱子里 → 找一双袜子要翻全屋。

多模块 = 衣服箱、书箱、厨具箱 → 找袜子直接开衣服箱,效率天差地别。

多模块的 4 个收益

收益 解释
构建提速 改 feature-user 不用编译 feature-payment;并行编译多个模块
边界清晰 只能用别人 api 暴露的接口,杜绝乱调用底层细节
团队解耦 用户组、订单组各自负责自己的模块,不互相踩
复用提升 lib-core 可被多个项目复用,单元测试也只需测核心模块

典型电商多模块依赖图

点击节点查看模块详情。注意:依赖必须是单向的,不能有环

appSpringBoot 启动
feature-user用户业务
feature-product商品业务
feature-order订单业务
feature-payment支付业务
lib-api接口契约
lib-core领域模型
lib-infraJDBC/Redis
app(启动模块) feature-*(业务模块) lib-api(公共契约) lib-core / lib-infra(基础库)
点击上方节点查看详情

📌 看图记 4 条铁律

  • 箭头方向:从消费者指向被依赖者(feature-user → lib-core 表示前者用了后者)。
  • app 在最顶层,谁都可以被它依赖;lib-* 在底层,不依赖任何 feature。
  • feature-payment → feature-order:是允许的(支付要查订单),但若反向依赖就成环了。
  • 所有 feature 都依赖 lib-api,但 lib-api 不依赖任何 feature → 单向依赖。

include 还是 includeBuild?

这是 Gradle 多项目最容易混淆的两个关键字,先看场景,再选用法。

用 include(同一构建)

// settings.gradle.kts
rootProject.name = "my-shop"
include(
    "app",
    "feature-user",
    "feature-order",
    "lib-core"
)

▸ 这些子模块都属于同一个 Gradle 构建,共享版本、插件、缓存。

子模块依赖示例

// feature-order/build.gradle.kts
dependencies {
    implementation(project(":lib-core"))
    implementation(project(":feature-user"))
}

▸ 用 project(":xxx") 引用,IDE 能直接跳转源码。

口诀:同仓库子模块 → include;独立工程拉来调试 → includeBuild;写 Convention Plugin → 用 includeBuild("build-logic")

多项目常用命令

多模块下 Gradle 命令的写法和单模块略有不同,下面是日常用得最多的几个。