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