【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
| 构建脚本 | 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 的配置管理体系。
如果本篇内容对你有帮助,欢迎点赞收藏!有任何疑问,欢迎在评论区交流。


