Skip to content

第 8 章 多项目构建 & Composite Build

学习目标:搞懂多模块项目的层级结构(root + sub-projects);会写 settings.gradle.kts 包含子模块;理解模块间依赖(project(":lib"));能用 Convention Plugin 抽公共配置;理解 Composite Build 的用途和它跟"多模块"的区别。


8.1 为什么要拆多模块?

8.1.1 单模块的痛

想象一个电商项目,所有代码堆在一个 src/main/java/

   ┌──────────────────────────────────────────────────────────┐
   │  monolithic-app/                                          │
   │  └── src/main/java/                                       │
   │      ├── com/example/auth/...        500 个类              │
   │      ├── com/example/billing/...     800 个类              │
   │      ├── com/example/notification/.. 300 个类              │
   │      ├── com/example/payment/...    1200 个类              │
   │      ├── com/example/web/...         400 个类              │
   │      └── ... 共 5000+ 个类                                 │
   │                                                              │
   │  痛点:                                                       │
   │  - 改 1 行 → 全量编译 5000 个类                              │
   │  - 团队协作:随便一个 commit 影响所有人                        │
   │  - 包之间没有"硬边界",调用混乱(auth 调 billing 内部细节)    │
   │  - 测试时间长,跑 1 个测试要等编译 5 分钟                     │
   └──────────────────────────────────────────────────────────┘

8.1.2 拆成多模块后

   ┌──────────────────────────────────────────────────────────┐
   │  multi-module-app/                                        │
   │  ├── settings.gradle.kts                                  │
   │  ├── app/                ← 入口模块                        │
   │  ├── auth/                                                 │
   │  ├── billing/                                              │
   │  ├── notification/                                         │
   │  ├── payment/                                              │
   │  ├── web/                                                  │
   │  └── lib-core/           ← 公共库                          │
   │                                                              │
   │  好处:                                                       │
   │  - 改 auth/ 里的代码 → 只重编 auth + 依赖它的模块             │
   │  - 多模块并行编译,速度提升 2-5 倍                            │
   │  - 模块间显式依赖,禁止"偷偷调内部"                           │
   │  - 不同团队可以独立维护各自模块                                │
   └──────────────────────────────────────────────────────────┘

📌 生活化类比:单模块像"所有人挤在一个大教室上课",老师讲一句全班都得听。多模块像"按年级分班",每班独立上课,互不打扰。


8.2 多模块项目的标准结构

8.2.1 推荐目录布局

my-app/
├── settings.gradle.kts           ← ① 入口:声明所有子模块
├── build.gradle.kts              ← ② 根 build(可选,一般空 / 只放 base)
├── gradle.properties
├── gradlew / gradlew.bat
├── gradle/
│   ├── libs.versions.toml        ← ③ Version Catalog
│   └── wrapper/

├── build-logic/                  ← ④ Convention Plugin(推荐)
│   └── convention/
│       └── src/main/kotlin/
│           ├── my-java-conventions.gradle.kts
│           ├── my-library-conventions.gradle.kts
│           └── my-application-conventions.gradle.kts

├── app/                          ← ⑤ 应用入口模块
│   ├── build.gradle.kts
│   └── src/main/...

├── lib-core/                     ← ⑥ 公共库
│   ├── build.gradle.kts
│   └── src/main/...

├── feature-billing/              ← ⑦ 业务模块
│   ├── build.gradle.kts
│   └── src/main/...

└── feature-payment/
    ├── build.gradle.kts
    └── src/main/...

8.2.2 settings.gradle.kts

kotlin
// settings.gradle.kts
pluginManagement {
    includeBuild("build-logic")     // ★ 包含 Convention Plugin
    repositories {
        gradlePluginPortal()
    }
}

dependencyResolutionManagement {
    repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
    repositories {
        mavenCentral()
        google()
    }
}

rootProject.name = "my-app"

// 包含所有子模块
include(
    "app",
    "lib-core",
    "feature-billing",
    "feature-payment"
)

// 改子模块的实际路径(非默认位置时)
project(":feature-billing").projectDir = file("features/billing")
project(":feature-payment").projectDir = file("features/payment")

8.3 模块间依赖

8.3.1 引用其他模块

kotlin
// app/build.gradle.kts
plugins {
    id("my.application-conventions")
}

dependencies {
    implementation(project(":lib-core"))           // ← 依赖 lib-core 模块
    implementation(project(":feature-billing"))    // ← 依赖 feature-billing 模块
    implementation(project(":feature-payment"))
}

8.3.2 lib-core 提供 API

kotlin
// lib-core/build.gradle.kts
plugins {
    id("my.library-conventions")    // 注意是 library
}

dependencies {
    api("org.apache.commons:commons-lang3:3.13.0")  // ← 用 api 让消费者也能用
    implementation("com.google.guava:guava:32.1.3-jre")  // ← 内部用,不传递
}

8.3.3 看模块间依赖图

bash
$ ./gradlew :app:dependencies --configuration runtimeClasspath

输出:

runtimeClasspath
+--- project :lib-core
|    \--- org.apache.commons:commons-lang3:3.13.0
+--- project :feature-billing
|    \--- project :lib-core (*)
\--- project :feature-payment
     \--- project :lib-core (*)

(*) 表示已经显示过,不再展开。


8.4 多模块的常见配置模式

8.4.1 反模式:subprojects {} 一统天下

老项目里常见这种写法:

kotlin
// ❌ 反模式:根 build.gradle.kts
subprojects {
    apply(plugin = "java")
    apply(plugin = "org.springframework.boot")    // 强加给所有模块!

    java { toolchain { languageVersion = JavaLanguageVersion.of(17) } }
    repositories { mavenCentral() }

    dependencies {
        "implementation"("org.springframework.boot:spring-boot-starter-web")
    }
}

问题

  1. 根 build 越写越长(500+ 行的项目随处可见)
  2. 所有模块被强制有 web 依赖(即使是个 lib)
  3. 配置缓存友好性差
  4. 编辑器难以做静态分析

8.4.2 推荐:Convention Plugin

kotlin
// build-logic/convention/src/main/kotlin/my-library-conventions.gradle.kts
plugins {
    java
}
java { toolchain { languageVersion = JavaLanguageVersion.of(17) } }
repositories { mavenCentral() }
dependencies {
    "testImplementation"("org.junit.jupiter:junit-jupiter:5.10.0")
}
tasks.named<Test>("test") { useJUnitPlatform() }

// build-logic/convention/src/main/kotlin/my-application-conventions.gradle.kts
plugins {
    id("my-library-conventions")           // 复用 library 配置
    application
    id("org.springframework.boot")
}

每个模块按需选:

kotlin
// app/build.gradle.kts
plugins { id("my-application-conventions") }

// lib-core/build.gradle.kts
plugins { id("my-library-conventions") }

💡 NowInAndroid 的 build-logic 就是这种结构。模块根据"是 application / library / feature / data"选不同 convention。


8.5 Composite Build:把多个独立项目"拼"起来

8.5.1 痛点

你在公司有 2 个独立项目:

  • my-app:业务应用
  • my-shared-lib:通用工具库(你自己也在维护)

正常流程:改 my-shared-lib → 发版本 1.2.3 → my-app 升级到 1.2.3 → 测试 → 发现 bug → 改 my-shared-lib → 发 1.2.4 → ...

痛点:每次都要发版,循环慢得想哭。

8.5.2 解法:Composite Build

my-shared-lib源码依赖包含进 my-app:

kotlin
// my-app/settings.gradle.kts
includeBuild("../my-shared-lib")
kotlin
// my-app/build.gradle.kts
dependencies {
    // 看起来还是 jar 依赖,但实际从 ../my-shared-lib 编译来
    implementation("com.example:shared-lib")
}

效果:

  • 改 my-shared-lib 的源码 → my-app 立刻能用最新代码
  • 不用再发版本调试
  • 调试时可以同时进 my-app 和 my-shared-lib 的源码

8.5.3 Composite Build vs 多模块

维度多模块(include)Composite Build(includeBuild)
项目层级1 个项目 N 个模块N 个独立项目
Git单仓库各自独立仓库
settings.gradle一份各自有
用途内部模块拆分跨项目源码调试

8.6 多模块项目典型场景

8.6.1 Spring Boot 微服务模块化

order-service/
├── order-api/               ← 接口定义(DTO、客户端)
├── order-domain/            ← 领域模型(纯 POJO)
├── order-service/           ← 业务逻辑
├── order-data/              ← 数据访问层
└── order-app/               ← 应用入口(Spring Boot main)

依赖方向:app → service → domain;data → domain;service → data。

8.6.2 Android 模块化

android-app/
├── app/                     ← 应用入口
├── core/
│   ├── designsystem/
│   ├── network/
│   └── data/
├── feature/
│   ├── home/
│   ├── search/
│   └── settings/
└── build-logic/

每个 feature 模块独立可编译可测试。

8.6.3 KMP 跨平台

kmp-app/
├── shared/                  ← 共享业务逻辑
│   ├── commonMain/
│   ├── androidMain/
│   ├── iosMain/
│   └── jvmMain/
├── androidApp/              ← Android 应用
└── iosApp/                  ← iOS 应用(Xcode 工程)

8.7 命令行操作多模块

8.7.1 跑指定模块

bash
# 跑根
$ ./gradlew build

# 跑指定子模块
$ ./gradlew :app:build
$ ./gradlew :lib-core:test

# 同时跑多个
$ ./gradlew :app:build :lib-core:build

# 给所有子模块跑同一个 task
$ ./gradlew test     # 等价于跑所有模块的 test

8.7.2 看项目结构

bash
$ ./gradlew projects

> Task :projects
Root project 'my-app'
+--- Project ':app'
+--- Project ':feature-billing'
+--- Project ':feature-payment'
\--- Project ':lib-core'

8.7.3 跨模块依赖

bash
$ ./gradlew :app:dependencies --configuration runtimeClasspath

8.8 章末小结

                    ★ 第 8 章核心知识图谱 ★

        ┌─────────────────────┼─────────────────────┐
        │                     │                     │
   ┌────▼────┐          ┌────▼────┐          ┌────▼─────┐
   │ 多模块   │          │ 公共配置  │          │ Composite│
   │ 拆分    │          │ 抽取      │          │ Build    │
   ├──────────┤          ├──────────┤          ├──────────┤
   │ settings │          │ Convention│          │ include  │
   │ include  │          │ Plugin    │          │  Build   │
   │ project()│          │ 抛弃      │          │ 跨项目   │
   │          │          │ subprojects│         │ 源码依赖 │
   └─────────┘          └──────────┘          └──────────┘

                   ★ build-logic 是工程化标配 ★

🎤 8.9 章末面试题(10 道高频题)

Q1. 多模块项目要拆成几个 module?拆分原则是什么?

:常见的拆分维度:

  1. 按业务功能:feature-auth、feature-billing、feature-payment
  2. 按层次:domain(领域)、data(数据访问)、service(业务)、web(接口)
  3. 按可复用性:core(公共库)、common-ui(公共 UI)
  4. 按部署单元:每个微服务一个模块

原则

  • 高内聚低耦合:模块内的类紧密关联,模块间通过显式 API 交互
  • 依赖单向:避免循环依赖(A → B、B → A)
  • 变化频率相近:经常一起改的代码放一个模块
  • 不要太细:3-5 个类的小模块意义不大,反而增加管理成本

Q2. include("app", "lib")includeBuild("../lib") 有什么区别?

  • include:包含子模块到当前 build。它们共享 root 项目的 settings 和上下文。
  • includeBuild:包含另一个独立的 Gradle 项目作为源码依赖。它有自己的 settings、自己的 build 命令。
维度includeincludeBuild
是同一个项目吗✅ 是❌ 不是
共享 settings
Git 仓库通常同一个通常不同
主要用途内部模块拆分跨项目源码调试

Q3. 模块 A 依赖模块 B,怎么写?

kotlin
// app/build.gradle.kts
dependencies {
    implementation(project(":lib-core"))           // 直接子模块
    implementation(project(":feature:billing"))    // 嵌套子模块(路径用 :)
}

project(":xxx") 里的 : 是模块路径分隔符。include("feature:billing") 在 settings 里对应这种嵌套写法。


Q4. subprojects {} 和 Convention Plugin 哪个好?

Convention Plugin 大胜。理由:

  1. 根 build 不用越写越长;
  2. 子模块按需 apply,不是被强制配置;
  3. Convention 自身可单元测试;
  4. 配置缓存友好;
  5. 跨项目可以复用;
  6. 这是 Google、Spring、Kotlin 等大型项目的实际做法。

subprojects {} 在小项目(< 5 模块)还可以用,但不推荐作为长期方案。


Q5. 多模块构建为什么比单模块快?

:3 个原因:

  1. 增量更精准:改 lib-core 只重编 lib-core + 依赖它的模块,不动其他模块;
  2. 并行编译:没有依赖关系的模块(如 feature-billing 和 feature-payment)同时跑
  3. 测试隔离:跑 :lib-core:test 只跑该模块的测试,不用跑全部。

实测:100 模块的 Android 项目,单模块改动从 60s 压到 3s,主要靠这套机制。


Q6. Composite Build 和 Maven 的 <modules> 一样吗?

不一样

  • Maven <modules> 类似 Gradle 的 include(多模块)—— 一个项目内的子模块。
  • Gradle includeBuild 是 Composite Build 概念,Maven 完全没有对应物
    • 它把另一个独立 Gradle 项目(有自己的 settings、自己 git)当源码依赖
    • 解决了"我改公共库时不用每次发包就能在主项目调试"的痛点

Maven 想做类似事情只能用 <dependency> + mvn install 一个个发到 local repo。


Q7. 多模块项目的根 build.gradle.kts 应该写什么?

:理想状态下应该几乎为空。所有子模块的配置都通过 Convention Plugin 注入。

如果非要写,可以放:

kotlin
plugins {
    base                 // clean 等聚合任务
}

// 全局任务(所有模块跑完后执行的报告等)
tasks.register("uploadAllReports") {
    dependsOn(subprojects.map { it.tasks.named("test") })
    doLast { /* 上传报告 */ }
}

反模式:在根 build 里堆 100 行 subprojects { ... }


Q8. 怎么避免子模块之间的循环依赖?

:3 个手段:

  1. 设计上避免:用第三方模块共享逻辑。比如 A 和 B 都要用某个 Util,把 Util 提取到 lib-core,A、B 都依赖 lib-core,不要 A、B 互相依赖

  2. 接口分离:A 需要调 B 的功能 → B 提供接口,A 实现回调。依赖方向变成 A → B 单向。

  3. 看依赖图找循环

bash
$ ./gradlew :app:dependencies | grep "FAILED\|circular"

Gradle 检测到循环依赖会直接报错(不是警告),所以一旦你写出循环依赖立刻就发现。


Q9. 为什么 Android 项目都要拆模块化?

:3 个核心原因:

  1. 构建速度:一个大 module 改 1 个 Activity 编译 5 分钟;拆模块后只编 30 秒;
  2. APK 优化:Dynamic Feature 可以按需下载(动态特性模块);
  3. 代码组织:feature 模块互不干扰,团队并行开发不冲突。

NowInAndroid 是 Google 官方的模块化范例,建议精读它的 build.gradle.kts。


Q10. 一个项目要 100 个模块算多吗?

不一定。看场景:

  • 不算多:Google 的 Android 客户端 1000+ 模块;Cash App 的 iOS 600+ 模块。
  • 要小心:模块数过多会让构建图复杂、IDE 索引慢、新人理解难。

经验法则

  • 50 模块以下:随意拆
  • 50-200 模块:要 Convention Plugin、Build Cache、Configuration Cache 全开
  • 200+ 模块:需要专门的构建优化团队(可能要用 Bazel / Buck 等工具补充)

下一章 → 第 9 章 · 性能优化 →

🎬 可视化演示

演示加载缓慢或样式异常?点此在新标签页打开 ↗

💻 示例代码

txt
/*
 * app 启动模块
 * ------------------------------------------------------------
 * 整个工程的入口,把所有 feature 装配起来对外提供服务。
 * 这里用普通 application 插件演示,真实项目可以改成 spring-boot。
 */
plugins {
    id("shop.java-conventions")
    application
}

application {
    mainClass.set("com.shop.ShopApp")
}

dependencies {
    implementation(project(":lib-core"))
    implementation(project(":feature-user"))
    implementation(project(":feature-order"))
}
java
package com.shop;

import com.shop.order.OrderService;

/**
 * 启动类:演示多模块协作。
 * 运行方式:./gradlew :app:run
 */
public class ShopApp {
    public static void main(String[] args) {
        OrderService orderService = new OrderService();
        String result = orderService.createOrder(1001L, "MacBook Pro");
        System.out.println("=== ShopApp 启动成功 ===");
        System.out.println(result);
    }
}
txt
/*
 * build-logic/build.gradle.kts
 * ------------------------------------------------------------
 * 这里告诉 Gradle:本项目用来写"约定插件 (Convention Plugin)"。
 *  - 应用 kotlin-dsl 后,src/main/kotlin/*.gradle.kts 会被编译成插件
 *  - 子模块就能用 plugins { id("xxx-conventions") } 引用
 */

plugins {
    `kotlin-dsl`
}

repositories {
    gradlePluginPortal()
    mavenCentral()
}

// 如果 Convention Plugin 里要使用某个第三方 Gradle 插件,需要在这里把它当依赖加上
// dependencies {
//     implementation("org.springframework.boot:spring-boot-gradle-plugin:3.2.0")
// }
txt
/*
 * build-logic 自身的 settings 文件
 * ------------------------------------------------------------
 * build-logic 是一个"独立的 Gradle 构建",被主项目通过 includeBuild 引入。
 * 这里它只有自己一个项目,不需要 include。
 */

dependencyResolutionManagement {
    repositories {
        gradlePluginPortal()
        mavenCentral()
    }
}

rootProject.name = "build-logic"
txt
/*
 * shop.java-conventions.gradle.kts
 * ------------------------------------------------------------
 * 一个典型的 Convention Plugin:把"每个 Java 模块都该写的那一坨脚本"封装一次。
 * 子模块只需 plugins { id("shop.java-conventions") } 即可继承所有约定。
 *
 * 真实生活类比:公司给每位员工配的"标配电脑",统一系统、统一软件,
 * 入职就拿到一台不需要再装一遍。
 */

plugins {
    java
}

java {
    toolchain {
        languageVersion.set(JavaLanguageVersion.of(17))
    }
}

// 注意:这里用字符串引用配置("testImplementation"),
// 因为 Convention Plugin 的脚本中没有该配置的类型安全访问器。
dependencies {
    "implementation"("org.slf4j:slf4j-api:2.0.9")
    "testImplementation"("org.junit.jupiter:junit-jupiter:5.10.0")
    "testRuntimeOnly"("org.junit.platform:junit-platform-launcher")
}

tasks.named<Test>("test") {
    useJUnitPlatform()
}

tasks.withType<JavaCompile>().configureEach {
    options.encoding = "UTF-8"
    options.compilerArgs.addAll(listOf("-Xlint:unchecked", "-Xlint:deprecation"))
}

// 验证插件已生效
tasks.register("conventionInfo") {
    group = "verification"
    description = "证明 shop.java-conventions 已应用"
    doLast {
        println("✅ ${project.path} 已应用 shop.java-conventions")
        println("   - JDK 17 toolchain")
        println("   - JUnit 5 测试框架")
        println("   - UTF-8 编码")
    }
}
txt
/*
 * 第 8 章 · 根项目 build.gradle.kts
 * ------------------------------------------------------------
 * 重要原则:根项目不写业务逻辑!
 * 它的职责是:
 *  1) 给所有子模块提供"全局可见"的 task(聚合性的)
 *  2) 不给子模块强制注入构建逻辑(那应该由 build-logic 的 Convention Plugin 完成)
 */

// 一个跨模块的辅助 task:列出所有子模块
tasks.register("listModules") {
    group = "help"
    description = "列出本工程所有子模块"
    doLast {
        println("\n=== 本工程包含 ${subprojects.size} 个子模块 ===")
        subprojects.forEach { sub ->
            println("  • ${sub.path}  ->  ${sub.projectDir.relativeTo(rootDir)}")
        }
    }
}

// 一个聚合 task:清理所有子模块
tasks.register("cleanAll") {
    group = "build"
    description = "清理所有子模块的 build 产物"
    // 注意:这里使用 dependsOn 拉起所有子模块的 clean
    // 真实场景里,./gradlew clean 已经会自动这么做了,这里只为演示
    dependsOn(subprojects.map { it.tasks.matching { t -> t.name == "clean" } })
}
txt
/*
 * feature-order 子模块
 * ------------------------------------------------------------
 * 订单业务模块。依赖 lib-core,并依赖 feature-user(订单要查买家)。
 */
plugins {
    id("shop.java-conventions")
}

dependencies {
    implementation(project(":lib-core"))
    implementation(project(":feature-user"))
}
java
package com.shop.order;

import com.shop.core.User;
import com.shop.user.UserService;

/**
 * 订单服务:跨模块依赖示例。
 *  - 来自 lib-core: User
 *  - 来自 feature-user: UserService
 */
public class OrderService {

    private final UserService userService = new UserService();

    public String createOrder(long userId, String productName) {
        User buyer = userService.findById(userId);
        return "Order created for " + buyer + " buying " + productName;
    }
}
txt
/*
 * feature-user 子模块
 * ------------------------------------------------------------
 * 用户业务模块。依赖 lib-core 拿到 User 实体。
 *
 * 关键写法:implementation(project(":lib-core"))
 *   - project(":...") 表示"模块依赖",IDE 可直接跳转
 *   - 改了 lib-core,本模块也能即时感知
 */
plugins {
    id("shop.java-conventions")
}

dependencies {
    implementation(project(":lib-core"))
}
java
package com.shop.user;

import com.shop.core.User;

/**
 * 用户业务服务,演示跨模块依赖:直接 import 来自 lib-core 的 User。
 */
public class UserService {

    public User findById(long id) {
        return new User(id, "Alice-" + id);
    }

    public boolean isAdmin(User u) {
        return u.getName().startsWith("admin");
    }
}
txt
/*
 * lib-core 子模块
 * ------------------------------------------------------------
 * 公共领域模型层。被几乎所有 feature 模块依赖。
 *
 * 注意:
 *  - 应用了根项目通过 build-logic 提供的约定插件
 *  - 同时应用了 java-library 以支持 api/implementation 区分
 */
plugins {
    id("shop.java-conventions")
    `java-library`
}

dependencies {
    // api 表示:依赖会传递给 lib-core 的消费者
    // 比如 feature-user 也能直接用 commons-lang3
    api("org.apache.commons:commons-lang3:3.14.0")
}
java
package com.shop.core;

/**
 * 公共领域模型:用户实体。
 * 放在 lib-core 里,所有 feature 模块都可使用。
 */
public class User {
    private final long id;
    private final String name;

    public User(long id, String name) {
        this.id = id;
        this.name = name;
    }

    public long getId() { return id; }
    public String getName() { return name; }

    @Override
    public String toString() {
        return "User{" + "id=" + id + ", name='" + name + "'}";
    }
}
markdown
# 第 8 章 · 多项目构建示例

## 目录结构

ch08-multi-project-demo/ ├── settings.gradle.kts # ★ 拼装清单 + includeBuild("build-logic") ├── build.gradle.kts # 根项目(不放业务) ├── build-logic/ # ★ Convention Plugin 仓库 │ ├── settings.gradle.kts │ ├── build.gradle.kts # 应用 kotlin-dsl │ └── src/main/kotlin/ │ └── shop.java-conventions.gradle.kts # 共享构建约定 ├── app/ # 启动模块 │ ├── build.gradle.kts │ └── src/main/java/com/shop/ShopApp.java ├── feature-user/ # 用户业务 ├── feature-order/ # 订单业务(依赖 user) └── lib-core/ # 公共领域模型


## 跑起来

```bash
# 看有哪些子模块
./gradlew projects

# 编译所有
./gradlew build

# 单独构建一个模块(更快)
./gradlew :feature-order:build

# 查看模块依赖树
./gradlew :feature-order:dependencies --configuration runtimeClasspath

# 启动 app
./gradlew :app:run

# 调试 build-logic:先看它的 task
./gradlew -p build-logic tasks

看点

  1. build-logic 通过 includeBuild 在 settings 里被引入 → 子模块用 plugins { id("shop.java-conventions") } 即可继承所有约定。
  2. 跨模块依赖implementation(project(":lib-core")) 而不是 jar/maven 坐标。
  3. 每个子模块 都极薄 —— 因为公共逻辑都被 Convention Plugin 收编了。
  4. 变更范围 —— 改 feature-user 不会编译 feature-order 的 jar; 只有 lib-core 的改动会触发下游全链路重编。

```txt [settings.gradle.kts]
/*
 * 第 8 章 · 多项目构建示例 · settings.gradle.kts
 * ------------------------------------------------------------
 * 这是多模块项目的"目录",决定哪些子模块会参与构建。
 * 关键点:
 *  1) pluginManagement 里 includeBuild("build-logic") 引入构建逻辑
 *  2) include(...) 把若干子模块挂到根项目下
 *  3) dependencyResolutionManagement 统一管理仓库(强烈推荐)
 */

pluginManagement {
    // ★ 把 build-logic 当成"插件来源",子模块可直接 plugins { id("xxx") }
    includeBuild("build-logic")

    repositories {
        gradlePluginPortal()
        mavenCentral()
    }
}

dependencyResolutionManagement {
    // 强制所有依赖只能从这里声明的仓库下载,禁止子模块 build.gradle 里再写 repositories {}
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        mavenCentral()
        google()
    }
}

rootProject.name = "ch08-multi-project-demo"

// ★ include 写在 settings 里,这就是"哪些子项目属于本次构建"的清单
include(
    "app",
    "feature-user",
    "feature-order",
    "lib-core"
)

/*
 * 思考题:
 *  - 如果删掉 include("feature-order"),会怎样?
 *    → 该模块不会被识别为子项目,也不能再被 project(":feature-order") 引用。
 *  - 如果想把外面 ../my-payment-sdk 的项目作为源码引入呢?
 *    → 在 settings.gradle.kts 顶部加 includeBuild("../my-payment-sdk")
 */

app/build.gradle.kts ↗ · app/src/main/java/com/shop/ShopApp.java ↗ · build-logic/build.gradle.kts ↗ · build-logic/settings.gradle.kts ↗ · build-logic/src/main/kotlin/shop.java-conventions.gradle.kts ↗ · build.gradle.kts ↗ · feature-order/build.gradle.kts ↗ · feature-order/src/main/java/com/shop/order/OrderService.java ↗ · feature-user/build.gradle.kts ↗ · feature-user/src/main/java/com/shop/user/UserService.java ↗ · lib-core/build.gradle.kts ↗ · lib-core/src/main/java/com/shop/core/User.java ↗ · README.md ↗ · settings.gradle.kts ↗