主题
第 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")
}
}问题:
- 根 build 越写越长(500+ 行的项目随处可见)
- 所有模块被强制有 web 依赖(即使是个 lib)
- 配置缓存友好性差
- 编辑器难以做静态分析
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 # 等价于跑所有模块的 test8.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 runtimeClasspath8.8 章末小结
★ 第 8 章核心知识图谱 ★
│
┌─────────────────────┼─────────────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼─────┐
│ 多模块 │ │ 公共配置 │ │ Composite│
│ 拆分 │ │ 抽取 │ │ Build │
├──────────┤ ├──────────┤ ├──────────┤
│ settings │ │ Convention│ │ include │
│ include │ │ Plugin │ │ Build │
│ project()│ │ 抛弃 │ │ 跨项目 │
│ │ │ subprojects│ │ 源码依赖 │
└─────────┘ └──────────┘ └──────────┘
↑
★ build-logic 是工程化标配 ★🎤 8.9 章末面试题(10 道高频题)
Q1. 多模块项目要拆成几个 module?拆分原则是什么?
答:常见的拆分维度:
- 按业务功能:feature-auth、feature-billing、feature-payment
- 按层次:domain(领域)、data(数据访问)、service(业务)、web(接口)
- 按可复用性:core(公共库)、common-ui(公共 UI)
- 按部署单元:每个微服务一个模块
原则:
- 高内聚低耦合:模块内的类紧密关联,模块间通过显式 API 交互
- 依赖单向:避免循环依赖(A → B、B → A)
- 变化频率相近:经常一起改的代码放一个模块
- 不要太细:3-5 个类的小模块意义不大,反而增加管理成本
Q2. include("app", "lib") 和 includeBuild("../lib") 有什么区别?
答:
include:包含子模块到当前 build。它们共享 root 项目的 settings 和上下文。includeBuild:包含另一个独立的 Gradle 项目作为源码依赖。它有自己的 settings、自己的 build 命令。
| 维度 | include | includeBuild |
|---|---|---|
| 是同一个项目吗 | ✅ 是 | ❌ 不是 |
| 共享 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 大胜。理由:
- 根 build 不用越写越长;
- 子模块按需 apply,不是被强制配置;
- Convention 自身可单元测试;
- 配置缓存友好;
- 跨项目可以复用;
- 这是 Google、Spring、Kotlin 等大型项目的实际做法。
subprojects {} 在小项目(< 5 模块)还可以用,但不推荐作为长期方案。
Q5. 多模块构建为什么比单模块快?
答:3 个原因:
- 增量更精准:改 lib-core 只重编 lib-core + 依赖它的模块,不动其他模块;
- 并行编译:没有依赖关系的模块(如 feature-billing 和 feature-payment)同时跑;
- 测试隔离:跑
: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 个手段:
设计上避免:用第三方模块共享逻辑。比如 A 和 B 都要用某个 Util,把 Util 提取到 lib-core,A、B 都依赖 lib-core,不要 A、B 互相依赖。
接口分离:A 需要调 B 的功能 → B 提供接口,A 实现回调。依赖方向变成 A → B 单向。
看依赖图找循环:
bash
$ ./gradlew :app:dependencies | grep "FAILED\|circular"Gradle 检测到循环依赖会直接报错(不是警告),所以一旦你写出循环依赖立刻就发现。
Q9. 为什么 Android 项目都要拆模块化?
答:3 个核心原因:
- 构建速度:一个大 module 改 1 个 Activity 编译 5 分钟;拆模块后只编 30 秒;
- APK 优化:Dynamic Feature 可以按需下载(动态特性模块);
- 代码组织: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看点
- build-logic 通过
includeBuild在 settings 里被引入 → 子模块用plugins { id("shop.java-conventions") }即可继承所有约定。 - 跨模块依赖 用
implementation(project(":lib-core"))而不是 jar/maven 坐标。 - 每个子模块 都极薄 —— 因为公共逻辑都被 Convention Plugin 收编了。
- 变更范围 —— 改
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 ↗