第 10 章 · 实战项目:MiniShop 微店后端

4 个互动演示:脚手架步骤 / 模块结构 / CI 流水线 / 最佳实践对比。

从空目录到生产级工程:8 步走完

点击每张卡片标记完成,跟着指引走一遍。

0/8
1

初始化 Wrapper

gradle init --type basic --dsl kotlin
得到 gradlew 脚本和 wrapper 目录。

2

写 settings.gradle.kts

声明 includeBuild("build-logic") 和所有子模块 include(...)。

3

写 libs.versions.toml

把所有版本号集中到 gradle/libs.versions.toml,告别裸版本号。

4

开性能开关

gradle.properties 里 daemon / parallel / caching / configuration-cache 全开。

5

建 build-logic

编写 shop.java-conventions / shop.spring-conventions 等约定插件。

6

建子模块

每个子模块只 plugins {} + dependencies {},不再写公共逻辑。

7

写最小启动类

app 模块写一个 Spring Boot 启动类,./gradlew :app:bootRun 验证。

8

接入 CI

GitHub Actions / GitLab CI 跑 ./gradlew build,启用 Gradle 缓存。

完成全部 8 步即可获得一个企业级 Gradle 工程模板。

MiniShop 完整目录结构

带 ⭐ 的是关键文件,理解它们就理解了整个工程。

mini-shop/ ├── settings.gradle.kts ⭐ 拼装清单 + includeBuild("build-logic") ├── build.gradle.kts 根项目(聚合 task,不放业务) ├── gradle.properties ⭐ 性能全开(daemon/parallel/caching/conf-cache) ├── gradlew / gradlew.bat / gradle/ Gradle Wrapper ├── gradle/libs.versions.toml ⭐ Version Catalog(全工程版本管理) │ ├── build-logic/ ⭐ 共享构建逻辑(约定插件) │ ├── settings.gradle.kts │ ├── build.gradle.kts 应用 kotlin-dsl │ └── src/main/kotlin/ │ ├── shop.java-conventions.gradle.kts Java 通用约定 │ ├── shop.test-conventions.gradle.kts JUnit 测试约定 │ └── shop.spring-conventions.gradle.kts SpringBoot 约定 │ ├── app/ 启动模块(只装配,不业务) │ ├── build.gradle.kts apply shop.spring-conventions │ └── src/main/java/com/shop/MiniShopApp.java │ ├── feature-user/ 用户域 │ ├── build.gradle.kts │ └── src/main/java/com/shop/user/UserController.java │ ├── feature-order/ 订单域(依赖 user) │ ├── build.gradle.kts │ └── src/main/java/com/shop/order/OrderController.java │ ├── lib-domain/ 公共领域模型(叶子节点) │ ├── build.gradle.kts │ └── src/main/java/com/shop/domain/User.java │ ├── .github/workflows/ │ └── ci.yml ⭐ CI 流水线 │ └── .gitignore 忽略 build/ .gradle/ 但保留 wrapper

✓ 你应该看到的

每个 build.gradle.kts 都很短(5-10 行);公共逻辑都在 build-logic 里;版本号都在 toml 里。

✗ 反例代码味道

某个 feature 模块的 build.gradle.kts 写了 200 行;4 个模块各自写一遍 toolchain;版本号在多处硬编码。

CI 流水线动画演示

点击下方按钮模拟一次 GitHub Actions 触发。

📥
Checkout
--
Setup JDK 17
--
📦
Restore Cache
--
🔨
./gradlew build
--
🧪
Tests
--
🚀
Publish
--
[等待触发...]

最佳实践 vs 常见反模式

左红右绿:照右边写就对了。

🏷️
❌ 反模式:版本号到处写
// app/build.gradle.kts
implementation("org.springframework.boot:spring-boot-starter-web:3.2.0")
// feature-user/build.gradle.kts
implementation("org.springframework.boot:spring-boot-starter-web:3.1.5")
// → 不同模块版本不一致!
✅ 推荐:Version Catalog
// gradle/libs.versions.toml
[versions]
spring-boot = "3.2.0"

// build.gradle.kts
implementation(libs.spring.boot.starter.web)
// → 版本永远一致,IDE 自动补全
🧱
❌ 反模式:根项目 subprojects {} 撒糖
// build.gradle.kts (根)
subprojects {
    apply(plugin = "java")
    java { toolchain { ... } }
    repositories { mavenCentral() }
    dependencies {
        "implementation"("...")
    }
    // ↑ 60 行 boilerplate
}
✅ 推荐:Convention Plugin
// build-logic/.../shop.java-conventions.gradle.kts
plugins { java }
java { toolchain { ... } }
dependencies { ... }

// 子模块只需:
plugins { id("shop.java-conventions") }
🔌
❌ 反模式:apply plugin "older-style"
buildscript {
    dependencies {
        classpath("org.springframework.boot:...:3.2.0")
    }
}
apply(plugin = "org.springframework.boot")
// → 老旧 + 不能享受 plugins {} DSL 的好处
✅ 推荐:plugins {} DSL
plugins {
    id("org.springframework.boot") version "3.2.0"
}
// 配合 settings.gradle.kts 的 pluginManagement,
// 还可以集中管理插件版本
🚦
❌ 反模式:CI 没缓存
# GitHub Actions
- run: ./gradlew build
# → 每次都重下所有依赖
# → 5 分钟变 25 分钟
✅ 推荐:用 setup-gradle action
- uses: gradle/actions/setup-gradle@v3
  with:
    cache-disabled: false
- run: ./gradlew build --scan
# → 自动缓存 Gradle Home + Build Cache
🧪
❌ 反模式:测试报告没显示
tasks.test {
    useJUnitPlatform()
}
// 测试失败时控制台只看到一行
// 必须打开 build/reports/tests 才能查
✅ 推荐:testLogging 详细输出
tasks.test {
    useJUnitPlatform()
    testLogging {
        events("passed", "skipped", "failed")
        exceptionFormat = TestExceptionFormat.FULL
        showStandardStreams = true
    }
}