欢迎光临
我们一直在努力

第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

维度MavenGradle
构建脚本 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-fkjKGnIGbTYDzb0A{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-fkjKGnIGbTYDzb0A .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fkjKGnIGbTYDzb0A .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fkjKGnIGbTYDzb0A .error-icon{fill:#552222;}#mermaid-svg-fkjKGnIGbTYDzb0A .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fkjKGnIGbTYDzb0A .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fkjKGnIGbTYDzb0A .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fkjKGnIGbTYDzb0A .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fkjKGnIGbTYDzb0A .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fkjKGnIGbTYDzb0A .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fkjKGnIGbTYDzb0A .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fkjKGnIGbTYDzb0A .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fkjKGnIGbTYDzb0A .marker.cross{stroke:#333333;}#mermaid-svg-fkjKGnIGbTYDzb0A svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fkjKGnIGbTYDzb0A p{margin:0;}#mermaid-svg-fkjKGnIGbTYDzb0A .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-fkjKGnIGbTYDzb0A .cluster-label text{fill:#333;}#mermaid-svg-fkjKGnIGbTYDzb0A .cluster-label span{color:#333;}#mermaid-svg-fkjKGnIGbTYDzb0A .cluster-label span p{background-color:transparent;}#mermaid-svg-fkjKGnIGbTYDzb0A .label text,#mermaid-svg-fkjKGnIGbTYDzb0A span{fill:#333;color:#333;}#mermaid-svg-fkjKGnIGbTYDzb0A .node rect,#mermaid-svg-fkjKGnIGbTYDzb0A .node circle,#mermaid-svg-fkjKGnIGbTYDzb0A .node ellipse,#mermaid-svg-fkjKGnIGbTYDzb0A .node polygon,#mermaid-svg-fkjKGnIGbTYDzb0A .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fkjKGnIGbTYDzb0A .rough-node .label text,#mermaid-svg-fkjKGnIGbTYDzb0A .node .label text,#mermaid-svg-fkjKGnIGbTYDzb0A .image-shape .label,#mermaid-svg-fkjKGnIGbTYDzb0A .icon-shape .label{text-anchor:middle;}#mermaid-svg-fkjKGnIGbTYDzb0A .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-fkjKGnIGbTYDzb0A .rough-node .label,#mermaid-svg-fkjKGnIGbTYDzb0A .node .label,#mermaid-svg-fkjKGnIGbTYDzb0A .image-shape .label,#mermaid-svg-fkjKGnIGbTYDzb0A .icon-shape .label{text-align:center;}#mermaid-svg-fkjKGnIGbTYDzb0A .node.clickable{cursor:pointer;}#mermaid-svg-fkjKGnIGbTYDzb0A .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-fkjKGnIGbTYDzb0A .arrowheadPath{fill:#333333;}#mermaid-svg-fkjKGnIGbTYDzb0A .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fkjKGnIGbTYDzb0A .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fkjKGnIGbTYDzb0A .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fkjKGnIGbTYDzb0A .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-fkjKGnIGbTYDzb0A .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fkjKGnIGbTYDzb0A .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-fkjKGnIGbTYDzb0A .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fkjKGnIGbTYDzb0A .cluster text{fill:#333;}#mermaid-svg-fkjKGnIGbTYDzb0A .cluster span{color:#333;}#mermaid-svg-fkjKGnIGbTYDzb0A 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-fkjKGnIGbTYDzb0A .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-fkjKGnIGbTYDzb0A rect.text{fill:none;stroke-width:0;}#mermaid-svg-fkjKGnIGbTYDzb0A .icon-shape,#mermaid-svg-fkjKGnIGbTYDzb0A .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fkjKGnIGbTYDzb0A .icon-shape p,#mermaid-svg-fkjKGnIGbTYDzb0A .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-fkjKGnIGbTYDzb0A .icon-shape .label rect,#mermaid-svg-fkjKGnIGbTYDzb0A .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fkjKGnIGbTYDzb0A .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-fkjKGnIGbTYDzb0A .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-fkjKGnIGbTYDzb0A :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

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

rootProject.name = "mini-shop"

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


三、build.gradle.kts 逐行详解

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

// ==================== 插件区块 ====================
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——声明依赖仓库

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


四、依赖配置详解

Gradle 的依赖配置形成了一个树状结构,决定了依赖的可见范围。下面这张图清晰展示了各配置的作用域:

#mermaid-svg-tphxEpvWcLrXZYtc{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-tphxEpvWcLrXZYtc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tphxEpvWcLrXZYtc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tphxEpvWcLrXZYtc .error-icon{fill:#552222;}#mermaid-svg-tphxEpvWcLrXZYtc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tphxEpvWcLrXZYtc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tphxEpvWcLrXZYtc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tphxEpvWcLrXZYtc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tphxEpvWcLrXZYtc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tphxEpvWcLrXZYtc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tphxEpvWcLrXZYtc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tphxEpvWcLrXZYtc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tphxEpvWcLrXZYtc .marker.cross{stroke:#333333;}#mermaid-svg-tphxEpvWcLrXZYtc svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tphxEpvWcLrXZYtc p{margin:0;}#mermaid-svg-tphxEpvWcLrXZYtc .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-tphxEpvWcLrXZYtc .cluster-label text{fill:#333;}#mermaid-svg-tphxEpvWcLrXZYtc .cluster-label span{color:#333;}#mermaid-svg-tphxEpvWcLrXZYtc .cluster-label span p{background-color:transparent;}#mermaid-svg-tphxEpvWcLrXZYtc .label text,#mermaid-svg-tphxEpvWcLrXZYtc span{fill:#333;color:#333;}#mermaid-svg-tphxEpvWcLrXZYtc .node rect,#mermaid-svg-tphxEpvWcLrXZYtc .node circle,#mermaid-svg-tphxEpvWcLrXZYtc .node ellipse,#mermaid-svg-tphxEpvWcLrXZYtc .node polygon,#mermaid-svg-tphxEpvWcLrXZYtc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tphxEpvWcLrXZYtc .rough-node .label text,#mermaid-svg-tphxEpvWcLrXZYtc .node .label text,#mermaid-svg-tphxEpvWcLrXZYtc .image-shape .label,#mermaid-svg-tphxEpvWcLrXZYtc .icon-shape .label{text-anchor:middle;}#mermaid-svg-tphxEpvWcLrXZYtc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tphxEpvWcLrXZYtc .rough-node .label,#mermaid-svg-tphxEpvWcLrXZYtc .node .label,#mermaid-svg-tphxEpvWcLrXZYtc .image-shape .label,#mermaid-svg-tphxEpvWcLrXZYtc .icon-shape .label{text-align:center;}#mermaid-svg-tphxEpvWcLrXZYtc .node.clickable{cursor:pointer;}#mermaid-svg-tphxEpvWcLrXZYtc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tphxEpvWcLrXZYtc .arrowheadPath{fill:#333333;}#mermaid-svg-tphxEpvWcLrXZYtc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tphxEpvWcLrXZYtc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tphxEpvWcLrXZYtc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tphxEpvWcLrXZYtc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tphxEpvWcLrXZYtc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tphxEpvWcLrXZYtc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tphxEpvWcLrXZYtc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tphxEpvWcLrXZYtc .cluster text{fill:#333;}#mermaid-svg-tphxEpvWcLrXZYtc .cluster span{color:#333;}#mermaid-svg-tphxEpvWcLrXZYtc 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-tphxEpvWcLrXZYtc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tphxEpvWcLrXZYtc rect.text{fill:none;stroke-width:0;}#mermaid-svg-tphxEpvWcLrXZYtc .icon-shape,#mermaid-svg-tphxEpvWcLrXZYtc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tphxEpvWcLrXZYtc .icon-shape p,#mermaid-svg-tphxEpvWcLrXZYtc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tphxEpvWcLrXZYtc .icon-shape .label rect,#mermaid-svg-tphxEpvWcLrXZYtc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tphxEpvWcLrXZYtc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tphxEpvWcLrXZYtc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tphxEpvWcLrXZYtc :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

这是最容易混淆的一对:

// 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 常用依赖配置速查

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 中生成版本目录:

[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 中使用

// 使用版本目录中的插件
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 会需要更多依赖。这里先把后续章节需要的依赖加上:

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 命令

# 编译项目
./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-mQty3xrJuWmHcoA6{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-mQty3xrJuWmHcoA6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mQty3xrJuWmHcoA6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mQty3xrJuWmHcoA6 .error-icon{fill:#552222;}#mermaid-svg-mQty3xrJuWmHcoA6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mQty3xrJuWmHcoA6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mQty3xrJuWmHcoA6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mQty3xrJuWmHcoA6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mQty3xrJuWmHcoA6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mQty3xrJuWmHcoA6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mQty3xrJuWmHcoA6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mQty3xrJuWmHcoA6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mQty3xrJuWmHcoA6 .marker.cross{stroke:#333333;}#mermaid-svg-mQty3xrJuWmHcoA6 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mQty3xrJuWmHcoA6 p{margin:0;}#mermaid-svg-mQty3xrJuWmHcoA6 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-mQty3xrJuWmHcoA6 .cluster-label text{fill:#333;}#mermaid-svg-mQty3xrJuWmHcoA6 .cluster-label span{color:#333;}#mermaid-svg-mQty3xrJuWmHcoA6 .cluster-label span p{background-color:transparent;}#mermaid-svg-mQty3xrJuWmHcoA6 .label text,#mermaid-svg-mQty3xrJuWmHcoA6 span{fill:#333;color:#333;}#mermaid-svg-mQty3xrJuWmHcoA6 .node rect,#mermaid-svg-mQty3xrJuWmHcoA6 .node circle,#mermaid-svg-mQty3xrJuWmHcoA6 .node ellipse,#mermaid-svg-mQty3xrJuWmHcoA6 .node polygon,#mermaid-svg-mQty3xrJuWmHcoA6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mQty3xrJuWmHcoA6 .rough-node .label text,#mermaid-svg-mQty3xrJuWmHcoA6 .node .label text,#mermaid-svg-mQty3xrJuWmHcoA6 .image-shape .label,#mermaid-svg-mQty3xrJuWmHcoA6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-mQty3xrJuWmHcoA6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mQty3xrJuWmHcoA6 .rough-node .label,#mermaid-svg-mQty3xrJuWmHcoA6 .node .label,#mermaid-svg-mQty3xrJuWmHcoA6 .image-shape .label,#mermaid-svg-mQty3xrJuWmHcoA6 .icon-shape .label{text-align:center;}#mermaid-svg-mQty3xrJuWmHcoA6 .node.clickable{cursor:pointer;}#mermaid-svg-mQty3xrJuWmHcoA6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mQty3xrJuWmHcoA6 .arrowheadPath{fill:#333333;}#mermaid-svg-mQty3xrJuWmHcoA6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mQty3xrJuWmHcoA6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mQty3xrJuWmHcoA6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mQty3xrJuWmHcoA6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mQty3xrJuWmHcoA6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mQty3xrJuWmHcoA6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mQty3xrJuWmHcoA6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mQty3xrJuWmHcoA6 .cluster text{fill:#333;}#mermaid-svg-mQty3xrJuWmHcoA6 .cluster span{color:#333;}#mermaid-svg-mQty3xrJuWmHcoA6 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-mQty3xrJuWmHcoA6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mQty3xrJuWmHcoA6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-mQty3xrJuWmHcoA6 .icon-shape,#mermaid-svg-mQty3xrJuWmHcoA6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mQty3xrJuWmHcoA6 .icon-shape p,#mermaid-svg-mQty3xrJuWmHcoA6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mQty3xrJuWmHcoA6 .icon-shape .label rect,#mermaid-svg-mQty3xrJuWmHcoA6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mQty3xrJuWmHcoA6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mQty3xrJuWmHcoA6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mQty3xrJuWmHcoA6 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

📁 子模块

📂 mini-shop(根项目)

implementation

implementation

api

include(…)

include(…)

include(…)

subprojects {}

subprojects {}

subprojects {}

🔗 依赖传递

settings.gradle.ktsrootProject.name = \\mini-shop\\

根 build.gradle.kts公共配置 + 插件管理

mini-shop-product产品模块

mini-shop-order订单模块

mini-shop-user用户模块

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

// settings.gradle.kts
rootProject.name = "mini-shop"
include("mini-shop-product")
include("mini-shop-order")
include("mini-shop-user")

// 根 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 的配置管理体系。


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

赞(0)
未经允许不得转载:171主机测评 » 第12篇-Gradle-Kotlin-DSL构建指南
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址