第12篇-Gradle-Kotlin-DSL构建指南

【Kotlin + Spring Boot 4 从零到架构师】第 12 篇:Gradle Kotlin DSL 构建指南

本系列定位:零基础入门,从 Kotlin 语法一路到 Spring Boot 4 高级架构(DDD + Modulith),适合 Java 开发者转型,也适合纯新手系统学习。


本篇你将学到

  • Gradle 构建系统的核心概念
  • build.gradle.kts 每个配置项的含义
  • 依赖配置 implementation / api / testImplementation / runtimeOnly 的区别
  • 版本目录(Version Catalog)统一管理依赖版本
  • 多模块项目构建基础

学完本篇,你将能独立阅读和编写 Gradle Kotlin DSL 构建脚本,理解 Spring Boot 项目的依赖管理体系。


一、Gradle 基础

1.1 Gradle vs Maven

维度 Maven Gradle
构建脚本 XML(pom.xml Groovy 或 Kotlin DSL
可编程性 低(XML 不支持逻辑) 高(完整编程语言)
构建速度 较慢 快(增量编译、构建缓存)
灵活性 低(约定严格) 高(可自定义任务)
Spring Boot 官方推荐 支持 首选(Spring Initializr 默认选项)

本系列使用 Gradle Kotlin DSL.kts 文件),因为:

  • 类型安全(IDE 有代码提示和编译检查)
  • 与 Kotlin 项目天然契合
  • Spring Boot 官方文档示例均为 Kotlin DSL

1.2 项目文件结构

复制代码
mini-shop/
├── settings.gradle.kts       ← 项目级设置(项目名、模块列表)
├── build.gradle.kts          ← 根构建脚本(依赖、插件、任务)
├── gradle/
│   ├── libs.versions.toml    ← 版本目录(统一管理依赖版本)
│   └── wrapper/              ← Gradle Wrapper(固定项目使用的 Gradle 版本)
├── gradlew                   ← Unix 系统的 Gradle Wrapper 脚本
├── gradlew.bat               ← Windows 的 Gradle Wrapper 脚本
└── src/                      ← 源代码目录

下面是 Gradle 构建的完整生命周期流程图,帮助你理解从编译到部署的整个过程:
#mermaid-svg-hNyEdwB6aNUBwwMk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-hNyEdwB6aNUBwwMk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hNyEdwB6aNUBwwMk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hNyEdwB6aNUBwwMk .error-icon{fill:#552222;}#mermaid-svg-hNyEdwB6aNUBwwMk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hNyEdwB6aNUBwwMk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hNyEdwB6aNUBwwMk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hNyEdwB6aNUBwwMk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hNyEdwB6aNUBwwMk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hNyEdwB6aNUBwwMk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hNyEdwB6aNUBwwMk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hNyEdwB6aNUBwwMk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hNyEdwB6aNUBwwMk .marker.cross{stroke:#333333;}#mermaid-svg-hNyEdwB6aNUBwwMk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hNyEdwB6aNUBwwMk p{margin:0;}#mermaid-svg-hNyEdwB6aNUBwwMk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-hNyEdwB6aNUBwwMk .cluster-label text{fill:#333;}#mermaid-svg-hNyEdwB6aNUBwwMk .cluster-label span{color:#333;}#mermaid-svg-hNyEdwB6aNUBwwMk .cluster-label span p{background-color:transparent;}#mermaid-svg-hNyEdwB6aNUBwwMk .label text,#mermaid-svg-hNyEdwB6aNUBwwMk span{fill:#333;color:#333;}#mermaid-svg-hNyEdwB6aNUBwwMk .node rect,#mermaid-svg-hNyEdwB6aNUBwwMk .node circle,#mermaid-svg-hNyEdwB6aNUBwwMk .node ellipse,#mermaid-svg-hNyEdwB6aNUBwwMk .node polygon,#mermaid-svg-hNyEdwB6aNUBwwMk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hNyEdwB6aNUBwwMk .rough-node .label text,#mermaid-svg-hNyEdwB6aNUBwwMk .node .label text,#mermaid-svg-hNyEdwB6aNUBwwMk .image-shape .label,#mermaid-svg-hNyEdwB6aNUBwwMk .icon-shape .label{text-anchor:middle;}#mermaid-svg-hNyEdwB6aNUBwwMk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-hNyEdwB6aNUBwwMk .rough-node .label,#mermaid-svg-hNyEdwB6aNUBwwMk .node .label,#mermaid-svg-hNyEdwB6aNUBwwMk .image-shape .label,#mermaid-svg-hNyEdwB6aNUBwwMk .icon-shape .label{text-align:center;}#mermaid-svg-hNyEdwB6aNUBwwMk .node.clickable{cursor:pointer;}#mermaid-svg-hNyEdwB6aNUBwwMk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-hNyEdwB6aNUBwwMk .arrowheadPath{fill:#333333;}#mermaid-svg-hNyEdwB6aNUBwwMk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hNyEdwB6aNUBwwMk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hNyEdwB6aNUBwwMk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hNyEdwB6aNUBwwMk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-hNyEdwB6aNUBwwMk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hNyEdwB6aNUBwwMk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-hNyEdwB6aNUBwwMk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hNyEdwB6aNUBwwMk .cluster text{fill:#333;}#mermaid-svg-hNyEdwB6aNUBwwMk .cluster span{color:#333;}#mermaid-svg-hNyEdwB6aNUBwwMk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-hNyEdwB6aNUBwwMk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hNyEdwB6aNUBwwMk rect.text{fill:none;stroke-width:0;}#mermaid-svg-hNyEdwB6aNUBwwMk .icon-shape,#mermaid-svg-hNyEdwB6aNUBwwMk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hNyEdwB6aNUBwwMk .icon-shape p,#mermaid-svg-hNyEdwB6aNUBwwMk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-hNyEdwB6aNUBwwMk .icon-shape .label rect,#mermaid-svg-hNyEdwB6aNUBwwMk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hNyEdwB6aNUBwwMk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hNyEdwB6aNUBwwMk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hNyEdwB6aNUBwwMk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 有测试
无测试
📁 源代码

(.kt / .kts)
编译

compileKotlin
测试阶段
运行测试

./gradlew test
打包

bootJar
📦 可执行 JAR

(build/libs/*.jar)
🚀 部署运行

java -jar app.jar

二、settings.gradle.kts

这是项目的入口文件,定义项目级别的基本信息:

kotlin 复制代码
rootProject.name = "mini-shop"

// 多模块项目时在此声明子模块
// include("domain", "application", "infrastructure")

三、build.gradle.kts 逐行详解

回顾 Spring Initializr 生成的构建文件:

kotlin 复制代码
// ==================== 插件区块 ====================
plugins {
    kotlin("jvm") version "2.2.0"
    kotlin("plugin.spring") version "2.2.0"
    id("org.springframework.boot") version "4.0.0"
    id("io.spring.dependency-management") version "1.1.7"
}

// ==================== 项目信息 ====================
group = "com.example"
version = "0.0.1-SNAPSHOT"

// ==================== Java 配置 ====================
java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

// ==================== Kotlin 配置 ====================
kotlin {
    compilerOptions {
        freeCompilerArgs.addAll("-Xjsr305=strict")
    }
}

// ==================== 仓库 ====================
repositories {
    mavenCentral()
}

// ==================== 依赖 ====================
dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    developmentOnly("org.springframework.boot:spring-boot-devtools")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

// ==================== 任务 ====================
tasks.withType<Test> {
    useJUnitPlatform()
}

3.1 插件详解

插件 作用
kotlin("jvm") 配置 Kotlin/JVM 编译器
kotlin("plugin.spring") 让 Spring 管理的类默认 open(Kotlin 类默认 final)
org.springframework.boot Spring Boot 构建:打包可执行 jar、依赖管理
io.spring.dependency-management 自动导入 Spring Boot 的 BOM(Bill of Materials),统一依赖版本

3.2 kotlin("plugin.spring") 详解

这个插件至关重要。Kotlin 的类和方法默认是 final 的,不能被继承。但 Spring 框架在很多场景下需要创建子类代理:

  • @Transactional 的事务代理
  • @Async 的异步代理
  • @Configuration 的配置代理

如果类是 final,CGLIB 就无法创建子类代理,功能就会失效。plugin.spring 会自动把以下注解标记的类改为 open

  • @Component 及衍生注解(@Service@Repository@Controller
  • @Async
  • @Transactional
  • @Configuration
  • @SpringBootApplication

3.3 repositories------声明依赖仓库

kotlin 复制代码
repositories {
    mavenCentral()                          // Maven 中央仓库
    // 如果有私服,可以添加
    // maven { url = uri("https://repo.company.com/maven2") }
}

四、依赖配置详解

Gradle 的依赖配置形成了一个树状结构,决定了依赖的可见范围。下面这张图清晰展示了各配置的作用域:
#mermaid-svg-prIPQ53ynjNwxise{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-prIPQ53ynjNwxise .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-prIPQ53ynjNwxise .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-prIPQ53ynjNwxise .error-icon{fill:#552222;}#mermaid-svg-prIPQ53ynjNwxise .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-prIPQ53ynjNwxise .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-prIPQ53ynjNwxise .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-prIPQ53ynjNwxise .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-prIPQ53ynjNwxise .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-prIPQ53ynjNwxise .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-prIPQ53ynjNwxise .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-prIPQ53ynjNwxise .marker{fill:#333333;stroke:#333333;}#mermaid-svg-prIPQ53ynjNwxise .marker.cross{stroke:#333333;}#mermaid-svg-prIPQ53ynjNwxise svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-prIPQ53ynjNwxise p{margin:0;}#mermaid-svg-prIPQ53ynjNwxise .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-prIPQ53ynjNwxise .cluster-label text{fill:#333;}#mermaid-svg-prIPQ53ynjNwxise .cluster-label span{color:#333;}#mermaid-svg-prIPQ53ynjNwxise .cluster-label span p{background-color:transparent;}#mermaid-svg-prIPQ53ynjNwxise .label text,#mermaid-svg-prIPQ53ynjNwxise span{fill:#333;color:#333;}#mermaid-svg-prIPQ53ynjNwxise .node rect,#mermaid-svg-prIPQ53ynjNwxise .node circle,#mermaid-svg-prIPQ53ynjNwxise .node ellipse,#mermaid-svg-prIPQ53ynjNwxise .node polygon,#mermaid-svg-prIPQ53ynjNwxise .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-prIPQ53ynjNwxise .rough-node .label text,#mermaid-svg-prIPQ53ynjNwxise .node .label text,#mermaid-svg-prIPQ53ynjNwxise .image-shape .label,#mermaid-svg-prIPQ53ynjNwxise .icon-shape .label{text-anchor:middle;}#mermaid-svg-prIPQ53ynjNwxise .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-prIPQ53ynjNwxise .rough-node .label,#mermaid-svg-prIPQ53ynjNwxise .node .label,#mermaid-svg-prIPQ53ynjNwxise .image-shape .label,#mermaid-svg-prIPQ53ynjNwxise .icon-shape .label{text-align:center;}#mermaid-svg-prIPQ53ynjNwxise .node.clickable{cursor:pointer;}#mermaid-svg-prIPQ53ynjNwxise .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-prIPQ53ynjNwxise .arrowheadPath{fill:#333333;}#mermaid-svg-prIPQ53ynjNwxise .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-prIPQ53ynjNwxise .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-prIPQ53ynjNwxise .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-prIPQ53ynjNwxise .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-prIPQ53ynjNwxise .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-prIPQ53ynjNwxise .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-prIPQ53ynjNwxise .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-prIPQ53ynjNwxise .cluster text{fill:#333;}#mermaid-svg-prIPQ53ynjNwxise .cluster span{color:#333;}#mermaid-svg-prIPQ53ynjNwxise div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-prIPQ53ynjNwxise .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-prIPQ53ynjNwxise rect.text{fill:none;stroke-width:0;}#mermaid-svg-prIPQ53ynjNwxise .icon-shape,#mermaid-svg-prIPQ53ynjNwxise .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-prIPQ53ynjNwxise .icon-shape p,#mermaid-svg-prIPQ53ynjNwxise .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-prIPQ53ynjNwxise .icon-shape .label rect,#mermaid-svg-prIPQ53ynjNwxise .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-prIPQ53ynjNwxise .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-prIPQ53ynjNwxise .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-prIPQ53ynjNwxise :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 🛠️ 开发工具
developmentOnly

✅ 开发时可见

❌ 打包时排除
🧪 测试模块
testImplementation

✅ 测试编译可见

✅ 测试运行时可见

❌ 主代码不可见
📦 主代码模块
推荐优先使用
implementation

✅ 编译可见

✅ 运行时可见

❌ 不传递
api

✅ 编译可见

✅ 运行时可见

✅ 传递
compileOnly

✅ 编译可见

❌ 运行时不可见
runtimeOnly

❌ 编译不可见

✅ 运行时可见

4.1 依赖配置的层级

Gradle 的依赖配置形成了一个树状结构,决定了依赖的可见范围

复制代码
implementation
  ├── 编译时可见 ✅
  └── 运行时可见 ✅
  └── 不传递给其他模块 ❌

api
  ├── 编译时可见 ✅
  ├── 运行时可见 ✅
  └── 传递给其他模块 ✅   ← 区别就在这里

compileOnly
  ├── 编译时可见 ✅
  └── 运行时不可见 ❌    (不打包进最终产物)

runtimeOnly
  ├── 编译时不可见 ❌
  └── 运行时可见 ✅

testImplementation
  ├── 测试编译时可见 ✅
  └── 主代码不可见 ❌

developmentOnly
  ├── 开发时可见 ✅(如 DevTools 热重载)
  └── 打包时排除 ❌

4.2 implementation vs api

这是最容易混淆的一对:

kotlin 复制代码
// implementation:依赖不传递
// 模块 A implementation("lib:X")
// 模块 B 依赖模块 A → B 看不到 lib:X 的 API

// api:依赖传递
// 模块 A api("lib:X")
// 模块 B 依赖模块 A → B 可以直接使用 lib:X 的 API

选择原则

场景 用哪个
需要在外部模块使用该依赖的 API api
仅在当前模块内部使用 implementation(绝大多数场景)

最佳实践 :优先用 implementation,只在必须暴露依赖时才用 api。这样能减少模块间的耦合,加快编译速度。

4.3 常用依赖配置速查

kotlin 复制代码
dependencies {
    // 主代码------编译+运行时都需要
    implementation("org.springframework.boot:spring-boot-starter-web")

    // 仅编译时需要(如 Lombok 注解处理器,Kotlin 项目一般不需要)
    compileOnly("org.projectlombok:lombok")

    // 仅运行时需要(如数据库驱动)
    runtimeOnly("org.postgresql:postgresql")

    // 开发工具(打包时排除)
    developmentOnly("org.springframework.boot:spring-boot-devtools")

    // 测试相关
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("io.mockk:mockk:1.13.13")
}

五、版本目录(Version Catalog)

5.1 为什么需要版本目录

当项目依赖越来越多时,直接在 build.gradle.kts 中写版本号会有两个问题:

  • 版本号散落在各处,升级时需要逐个搜索修改
  • 多模块项目中,不同模块可能用了不同版本,导致冲突

版本目录(Version Catalog)把所有依赖统一管理在一个文件中。

5.2 libs.versions.toml

Spring Initializr 默认在 gradle/libs.versions.toml 中生成版本目录:

toml 复制代码
[versions]
kotlin = "2.2.0"
springBoot = "4.0.0"
springDependencyManagement = "1.1.7"

[libraries]
spring-boot-starter-web = { group = "org.springframework.boot", name = "spring-boot-starter-web" }
kotlin-reflect = { group = "org.jetbrains.kotlin", name = "kotlin-reflect" }
spring-boot-starter-test = { group = "org.springframework.boot", name = "spring-boot-starter-test" }

[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-spring = { id = "org.jetbrains.kotlin.plugin.spring", version.ref = "kotlin" }
spring-boot = { id = "org.springframework.boot", version.ref = "springBoot" }
spring-dependency-management = { id = "io.spring.dependency-management", version.ref = "springDependencyManagement" }

5.3 在 build.gradle.kts 中使用

kotlin 复制代码
// 使用版本目录中的插件
plugins {
    alias(libs.plugins.kotlin.jvm)
    alias(libs.plugins.kotlin.spring)
    alias(libs.plugins.spring.boot)
    alias(libs.plugins.spring.dependency.management)
}

// 使用版本目录中的依赖
dependencies {
    implementation(libs.spring.boot.starter.web)
    implementation(libs.kotlin.reflect)
    testImplementation(libs.spring.boot.starter.test)
}

优势 :版本号集中在 libs.versions.toml 中维护,升级版本只需改一个文件。在多模块项目中尤为重要。


六、为 mini-shop 添加依赖

随着教程推进,mini-shop 会需要更多依赖。这里先把后续章节需要的依赖加上:

kotlin 复制代码
dependencies {
    // Spring Boot Starters
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-validation")

    // Kotlin
    implementation("org.jetbrains.kotlin:kotlin-reflect")

    // 数据库
    runtimeOnly("org.postgresql:postgresql")

    // 开发工具
    developmentOnly("org.springframework.boot:spring-boot-devtools")

    // 测试
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}
依赖 用途 引入时机
spring-boot-starter-web Web MVC + Tomcat 本篇
spring-boot-starter-data-jpa Spring Data JPA + Hibernate 模块三
spring-boot-starter-validation Bean Validation 模块二
postgresql PostgreSQL JDBC 驱动 模块三

七、常用 Gradle 命令

bash 复制代码
# 编译项目
./gradlew build

# 运行测试
./gradlew test

# 打包(不含测试)
./gradlew bootJar

# 清理构建产物
./gradlew clean

# 查看依赖树(排查冲突时非常有用)
./gradlew dependencies

# 刷新依赖(IDEA 中修改 build.gradle.kts 后同步)
./gradlew --refresh-dependencies build

Windows 用户 :把 ./gradlew 替换为 gradlew.bat


八、多模块项目预览

在后续 DDD + Modulith 章节中,项目会拆分为多个模块。下面是多模块项目的架构关系图:
#mermaid-svg-W3pWDJF3948rCWzN{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-W3pWDJF3948rCWzN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-W3pWDJF3948rCWzN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-W3pWDJF3948rCWzN .error-icon{fill:#552222;}#mermaid-svg-W3pWDJF3948rCWzN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-W3pWDJF3948rCWzN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-W3pWDJF3948rCWzN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-W3pWDJF3948rCWzN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-W3pWDJF3948rCWzN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-W3pWDJF3948rCWzN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-W3pWDJF3948rCWzN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-W3pWDJF3948rCWzN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-W3pWDJF3948rCWzN .marker.cross{stroke:#333333;}#mermaid-svg-W3pWDJF3948rCWzN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-W3pWDJF3948rCWzN p{margin:0;}#mermaid-svg-W3pWDJF3948rCWzN .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-W3pWDJF3948rCWzN .cluster-label text{fill:#333;}#mermaid-svg-W3pWDJF3948rCWzN .cluster-label span{color:#333;}#mermaid-svg-W3pWDJF3948rCWzN .cluster-label span p{background-color:transparent;}#mermaid-svg-W3pWDJF3948rCWzN .label text,#mermaid-svg-W3pWDJF3948rCWzN span{fill:#333;color:#333;}#mermaid-svg-W3pWDJF3948rCWzN .node rect,#mermaid-svg-W3pWDJF3948rCWzN .node circle,#mermaid-svg-W3pWDJF3948rCWzN .node ellipse,#mermaid-svg-W3pWDJF3948rCWzN .node polygon,#mermaid-svg-W3pWDJF3948rCWzN .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-W3pWDJF3948rCWzN .rough-node .label text,#mermaid-svg-W3pWDJF3948rCWzN .node .label text,#mermaid-svg-W3pWDJF3948rCWzN .image-shape .label,#mermaid-svg-W3pWDJF3948rCWzN .icon-shape .label{text-anchor:middle;}#mermaid-svg-W3pWDJF3948rCWzN .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-W3pWDJF3948rCWzN .rough-node .label,#mermaid-svg-W3pWDJF3948rCWzN .node .label,#mermaid-svg-W3pWDJF3948rCWzN .image-shape .label,#mermaid-svg-W3pWDJF3948rCWzN .icon-shape .label{text-align:center;}#mermaid-svg-W3pWDJF3948rCWzN .node.clickable{cursor:pointer;}#mermaid-svg-W3pWDJF3948rCWzN .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-W3pWDJF3948rCWzN .arrowheadPath{fill:#333333;}#mermaid-svg-W3pWDJF3948rCWzN .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-W3pWDJF3948rCWzN .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-W3pWDJF3948rCWzN .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-W3pWDJF3948rCWzN .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-W3pWDJF3948rCWzN .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-W3pWDJF3948rCWzN .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-W3pWDJF3948rCWzN .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-W3pWDJF3948rCWzN .cluster text{fill:#333;}#mermaid-svg-W3pWDJF3948rCWzN .cluster span{color:#333;}#mermaid-svg-W3pWDJF3948rCWzN div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-W3pWDJF3948rCWzN .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-W3pWDJF3948rCWzN rect.text{fill:none;stroke-width:0;}#mermaid-svg-W3pWDJF3948rCWzN .icon-shape,#mermaid-svg-W3pWDJF3948rCWzN .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-W3pWDJF3948rCWzN .icon-shape p,#mermaid-svg-W3pWDJF3948rCWzN .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-W3pWDJF3948rCWzN .icon-shape .label rect,#mermaid-svg-W3pWDJF3948rCWzN .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-W3pWDJF3948rCWzN .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-W3pWDJF3948rCWzN .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-W3pWDJF3948rCWzN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 📁 子模块
📂 mini-shop(根项目)
implementation
implementation
api
include(...)
include(...)
include(...)
subprojects {}
subprojects {}
subprojects {}
🔗 依赖传递
settings.gradle.kts

rootProject.name = \mini-shop\
根 build.gradle.kts

公共配置 + 插件管理
mini-shop-product

产品模块
mini-shop-order

订单模块
mini-shop-user

用户模块

在后续 DDD + Modulith 章节中,项目会拆分为多个模块。多模块项目的构建结构:

kotlin 复制代码
// settings.gradle.kts
rootProject.name = "mini-shop"
include("mini-shop-product")
include("mini-shop-order")
include("mini-shop-user")
kotlin 复制代码
// 根 build.gradle.kts(公共配置)
plugins {
    kotlin("jvm") apply false
    kotlin("plugin.spring") apply false
    id("org.springframework.boot") apply false
    id("io.spring.dependency-management") apply false
}

allprojects {
    group = "com.example"
    version = "0.0.1-SNAPSHOT"
}

subprojects {
    apply(plugin = "kotlin")
    apply(plugin = "org.springframework.boot")
    apply(plugin = "io.spring.dependency-management")

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

这部分只是预览,实际多模块构建会在模块八(高级架构)中详细讲解。


本篇小结

知识点 核心内容
Gradle Kotlin DSL 类型安全的构建脚本,Spring Boot 首选
kotlin("plugin.spring") 自动让 Spring 管理的类变为 open
implementation 依赖不传递(优先使用)
api 依赖传递给其他模块(少用)
compileOnly 仅编译时可见(不打包)
runtimeOnly 仅运行时可见(如数据库驱动)
developmentOnly 开发时可见(打包排除)
版本目录 libs.versions.toml 统一管理版本
./gradlew build 编译+测试+打包
./gradlew dependencies 查看依赖树排查冲突

下篇预告

第 13 篇:配置管理 --- application.yml 与 @ConfigurationProperties

如何优雅地管理配置?@Value@ConfigurationProperties 有什么区别?多环境怎么配置?下一篇全面讲解 Spring Boot 的配置管理体系。


如果本篇内容对你有帮助,欢迎点赞收藏!有任何疑问,欢迎在评论区交流。

相关推荐
大鱼>1 小时前
DSPy:LLM程序自动编译与提示词优化
开发语言·人工智能·python·深度学习
撩妹帝九歌1 小时前
Java文件写入与编码、字节数组、字符集、字符编解码 一文打通!
java·开发语言
星核0penstarry1 小时前
Solon AI v4.0.4 技术分析:Java Agent 框架的兼容性突破与选型考量 基于 OSCHINA 2026-07-30 报道及公开技术资料整理
java·开发语言·人工智能
昨夜星河入梦来2 小时前
postman接口测试报错503的解决方法
开发语言·postman
spider_xcxc2 小时前
Jenkins 遇见 Argo CD:构建可靠 GitOps 流水线的实战指南
java·开发语言·servlet
IT小盘2 小时前
16-Prompt版本管理-从手工修改到可追踪配置系统
开发语言·人工智能·python·prompt
Cx330❀3 小时前
【Linux网络】深入 HTTP 协议(五):从 Cookie/Session 原理到 C++ 源码实战
linux·运维·服务器·开发语言·网络·c++·http
keyipatience3 小时前
Socket编程3个版本
linux·运维·服务器·开发语言·网络·ubuntu
北冥you鱼3 小时前
Go语言四则运算实战:从基础类型到big包的深度解析
开发语言·后端·golang