既然能少写一半代码、还顺手灭掉 NPE,Spring 为什么不“更偏爱 Kotlin”一点呢?
🏆本文收录于《滚雪球学SpringBoot 3》:
https://blog.csdn.net/weixin_43970743/category_12795608.html,专门攻坚指数提升,本年度国内最系统+最专业+最详细(永久更新)。
本专栏致力打造最硬核 SpringBoot3 从零基础到进阶系列学习内容,🚀均为全网独家首发,打造精品专栏,专栏持续更新中…欢迎大家订阅持续学习。 如果想快速定位学习,可以看这篇【SpringBoot3教程导航帖】https://blog.csdn.net/weixin_43970743/article/details/151115907,你想学习的都被收集在内,快速投入学习!!两不误。
若还想学习更多,可直接前往《滚雪球学SpringBoot(全版本合集)》:https://blog.csdn.net/weixin_43970743/category_11599389.html,涵盖SpringBoot所有版本教学文章。
演示环境说明:
- 开发工具:IDEA 2021.3
- JDK版本: JDK 17(推荐使用 JDK 17 或更高版本,因为 Spring Boot 3.x 系列要求 Java 17,Spring Boot 3.5.4 基于 Spring Framework 6.x 和 Jakarta EE 9,它们都要求至少 JDK 17。)
- Spring Boot版本:3.5.4(于25年7月24日发布)
- Maven版本:3.8.2 (或更高)
- Gradle:(如果使用 Gradle 构建工具的话):推荐使用 Gradle 7.5 或更高版本,确保与 JDK 17 兼容。
- 操作系统:Windows 11
前言:Kotlin 不是“更好看的 Java”,它是让你少犯错、少写废话的工具(而且 Spring 还真配合)
很多人对 Kotlin 的第一印象就是:
“哦,语法糖嘛,写起来短一点。”
但你真写一阵会发现:它的价值不止是短,更多是——把一堆原本要靠自律、靠 code review、靠祈祷才能守住的底线,直接塞进编译器里。
Spring 官方文档也不藏着掖着:Spring Framework 的 Kotlin 文档里明确说,Spring Boot 是构建 Kotlin + Spring 应用最简单的方式,而且参考文档里很多示例同时给 Java 与 Kotlin。
Spring Boot 参考文档也单独开了一章讲 Kotlin 支持,说明这是“认真对待”的一等公民,而不是“顺便能跑”。
好,那我们就开拆。
1) 为什么 Spring 官方偏爱 Kotlin?
这句“偏爱”不是我瞎煽情,Spring 生态这些年确实在持续做 Kotlin 友好化:文档、扩展、DSL、空安全、协程适配……一整套。你可以从几个官方信号看出来。
1.1 官方文档把 Kotlin 当“默认选项之一”,不是彩蛋
- Spring Framework Kotlin 总览里强调:用 Kotlin 构建 Spring 应用最简单的方式就是 Spring Boot,并引导去
start.spring.io走 Kotlin 路线。 - Spring Boot 直接有 “Kotlin Support” 章节,明确 Boot 是通过 Spring Framework、Spring Data、Reactor 等项目的支持来提供 Kotlin 体验。
- Kotlin 官方文档也有专门的 Spring Boot 教程,把它当成“服务器端 Kotlin”主线之一。
这意味着:你不是在走野路子,你走的是“官方铺好的路”。
1.2 Kotlin 解决了 Spring 项目里最常见的两类痛点
痛点 A:样板代码太多
- DTO、构造器、Getter/Setter、Builder、重载……这些 Kotlin 直接砍掉一大片(尤其 data class + 默认参数)。
- 构造器注入在 Kotlin 里几乎“天然顺滑”,后面一节你会看到有多省心。
痛点 B:空指针与可空语义不清
Spring Framework 专门写了一章 Null-safety:Kotlin 的核心优势就是“编译期处理 null”,避免运行时撞上经典 NPE。
而 Spring 官方博客也提到,通过 JSpecify 等空性元数据的推进,Spring API 在 Kotlin 下可以变得更“惯用且空安全”。
一句话总结:Kotlin 把 Spring 开发里最磨人的两坨(废话 + NPE)都削薄了。
2) 构造器注入的简化:主构造函数=最自然的依赖注入
如果你写过 Java 版 Spring,你一定见过这种“经典套路”:
- 一堆
private final字段 - 一个构造器
- 然后 IDE 帮你生成
- 再然后你改了依赖,构造器参数顺序和字段又要调
Kotlin 在这件事上简直像开了挂:主构造函数直接就是你的依赖列表。
2.1 Java 风格 vs Kotlin 风格(同一件事,Kotlin 少一半噪音)
Kotlin(推荐写法):
import org.springframework.stereotype.Service
@Service
class OrderService(
private val pricingClient: PricingClient,
private val inventoryClient: InventoryClient,
) {
fun placeOrder(sku: String, qty: Int): String {
val price = pricingClient.quote(sku)
inventoryClient.reserve(sku, qty)
return "OK price=$price"
}
}
你看到了吗:
- 依赖就是构造参数
- 字段就是参数前面加
private val - 没有样板构造器,没有 Lombok,没有“为了注入而注入”的仪式感
小情绪:我以前写 Java 版构造器注入,经常有一种“我是在写业务,还是在写字段管理?”的迷惑感。Kotlin 这点是真的解脱。
2.2 “那我还需要 @Autowired 吗?”
通常不需要。Spring 的推荐方向本来就是构造器注入优先,而 Kotlin 写法本身就把构造器摆在脸上。
你会发现:依赖从“隐藏在字段注入里”变成“显式写在类签名里”,可读性直接上一个台阶。
3) Null Safety:消灭 NPE,不靠祈祷靠类型系统
Spring Framework 的 Null-safety 章节开头就点题:Kotlin 的 null-safety 能在编译期清晰处理 null,而不是运行时撞上 NullPointerException。
这句话很“官方”,我给你翻译成“写代码的人话”:
Kotlin 逼你在写代码的时候就想清楚:这个值到底可能为 null 吗?
不想清楚?对不起,编译不过。
3.1 一个最常见的 NPE 来源:Controller 入参 / 查询结果
你用 Java 时可能这么写(伪代码):
- 请求参数缺失 →
null - 查库没查到 →
null - 然后你
.getXxx()→ boom
Kotlin 的策略更“冷酷”:
String表示“绝不为 null”String?表示“可能为 null”
于是很多坑会被你提前堵住。
3.2 Kotlin DTO:把“可空”写进类型里,代码审查更轻松
data class CreateUserRequest(
val email: String, // 必填
val nickname: String?, // 可选
)
- 看到
String?,你不用猜它能不能为 null - 看到
String,你也不用猜它是不是“约定必填但可能空”
3.3 Spring API 的空空性信息:Kotlin 能吃到更多“空安全收益”
Spring 官方博客提到,随着 JSpecify 等元数据的使用推进,Spring API 在 Kotlin 下会更趋向“惯用的空安全”。
再结合 Spring Framework 的 Null-safety 文档,你会发现 Spring 对 Kotlin 的空性适配不是一句口号,是在持续补齐“类型语义”。
真实感受:当你开始依赖这些空性信息后,IDE 的提示会变得非常“唠叨”,但这种唠叨往往是在救命。
4) Kotlin Coroutines(协程)在 Controller 和 Repository 中的使用
协程这块特别容易被讲成玄学:
有人把它当“更好写的异步”,有人把它当“响应式替代品”,还有人一上来就 runBlocking 然后把系统堵死……
我们按 Spring 官方文档的语境来:Spring Framework 有专门的 Coroutines 章节,重点在于 挂起函数的执行上下文、与 Reactor 的适配、以及上下文传播(traceId/observation)。
4.1 Controller:suspend fun 让接口代码像同步一样好读
这里我先给出最“顺滑”的用法:在支持协程的 Spring Web 栈里,Controller 方法可以写成
suspend fun,避免回调地狱,让异步逻辑更线性。Spring 的协程文档说明了 Spring 如何将协程挂起函数适配到 Reactor 类型,并提到上下文传播需要额外关注。
示例:协程 Controller(返回单个对象)
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RestController
data class UserDto(val id: String, val name: String)
@RestController
class UserController(
private val userService: UserService
) {
@GetMapping("/users/{id}")
suspend fun getUser(@PathVariable id: String): UserDto {
// 看起来像同步,其实可以是非阻塞挂起
return userService.findUser(id)
}
}
Service:依然是 suspend,链路就很干净
import org.springframework.stereotype.Service
@Service
class UserService(
private val userRepository: UserRepository
) {
suspend fun findUser(id: String): UserDto {
val user = userRepository.findById(id) ?: throw NoSuchElementException("user not found")
return UserDto(user.id, user.name)
}
}
你会发现:
- 没有
Mono<UserDto>/Flux<UserDto>的包装噪音 - 错误处理可以用普通
try/catch或抛异常 - 读起来像同步,但可以保持“挂起即等待”的非阻塞语义(具体取决于你底层是否是真非阻塞)
4.2 Repository:Spring Data 的协程仓库(Coroutines Repository)
Spring Data Relational 的官方文档说得很直接:协程仓库建立在 reactive repositories 之上,通过 Kotlin 协程暴露非阻塞的数据访问,方法可以是 suspend 或返回 Flow。
示例:CoroutineCrudRepository(R2DBC/Relational 场景常见)
import org.springframework.data.annotation.Id
import org.springframework.data.relational.core.mapping.Table
import org.springframework.data.repository.kotlin.CoroutineCrudRepository
import kotlinx.coroutines.flow.Flow
@Table("users")
data class User(
@Id val id: String,
val name: String
)
interface UserRepository : CoroutineCrudRepository<User, String> {
suspend fun findByName(name: String): User?
fun findAllByName(name: String): Flow<User>
}
然后 Service 里就能非常“线性”地写:
import kotlinx.coroutines.flow.toList
import org.springframework.stereotype.Service
@Service
class QueryService(private val repo: UserRepository) {
suspend fun findOne(name: String): User =
repo.findByName(name) ?: throw NoSuchElementException("not found")
suspend fun findMany(name: String): List<User> =
repo.findAllByName(name).toList()
}
4.3 一个必须提醒的坑:协程≠自动非阻塞,别把阻塞调用塞进协程里装无辜
协程能让你写得像同步,但底层是不是非阻塞,取决于你用的是什么:
- 如果你的 Repository 是协程 + reactive 驱动(比如基于 reactive repository 的 coroutines repository),那链路才更像“真非阻塞”。
- 如果你底层还是 JDBC(阻塞 I/O),你把它包成
suspend也只是“语法上好看”,并不会凭空变快——最多是把阻塞藏得更深。
另外 Spring Framework 协程文档强调了上下文传播(例如 traceId)在挂起函数执行上下文中的问题,需要通过 Micrometer context-propagation 等机制配合。
这点在生产排障时非常要命:你以为日志会自动带 traceId,结果协程切走了上下文,日志突然“失忆”,那种感觉真的很想掀桌😇。
5) 一套“能直接起项目”的最小示例(Gradle Kotlin DSL)
你要是从零起步,Kotlin 官方就有 Spring Boot 教程入口,走官方路线最省心。
我这里给一个“常见组合”的 build.gradle.kts 轮廓(根据你用 WebFlux 还是 MVC、用 R2DBC 还是 JPA 再微调):
plugins {
kotlin("jvm") version "2.0.0"
kotlin("plugin.spring") version "2.0.0"
id("org.springframework.boot") version "3.3.0"
id("io.spring.dependency-management") version "1.1.6"
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web") // 或 webflux
implementation("org.springframework.boot:spring-boot-starter-data-r2dbc") // 协程+非阻塞更搭
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core")
}
你具体版本以你项目的 Spring Boot 版本线为准;如果你走 Spring 官方 Kotlin 指南,也会引导你从
start.spring.io生成更匹配的依赖组合。
结语:Spring + Kotlin 的“合”,其实合在三件事上
如果让我用不那么鸡汤、但更真实的方式总结:
- 合在可读性:构造器注入 + data class + 默认参数,让“业务意图”更显眼
- 合在安全性:Null Safety 把一堆“隐性约定”变成“显式类型”
- 合在并发表达力:协程让异步逻辑更像人写的,但别忘了上下文传播与底层阻塞/非阻塞的边界
说白了就是:你少写废话、少靠运气、多靠编译器和框架给你兜住底线。
这感觉,就像从“手动挡爬坡”换成“自动挡+防溜坡”——不是你技术变差了,是你终于不用把精力浪费在不值钱的事上了😌。
🧧福利赠与你🧧
无论你是计算机专业的学生,还是对编程有兴趣的小伙伴,都建议直接毫无顾忌的学习此专栏「滚雪球学SpringBoot」,bug菌郑重承诺,凡是学习此专栏的同学,均能获取到所需的知识和技能,全网最快速入门SpringBoot,就像滚雪球一样,越滚越大, 无边无际,指数级提升。
最后,如果这篇文章对你有所帮助,帮忙给作者来个一键三连,关注、点赞、收藏,您的支持就是我坚持写作最大的动力。
同时欢迎大家关注公众号:「猿圈奇妙屋」 ,以便学习更多同类型的技术文章,免费白嫖最新BAT互联网公司面试题、4000G PDF编程电子书、简历模板、技术文章Markdown文档等海量资料。
ps:本文涉及所有源代码,均已上传至Gitee:
https://gitee.com/bugjun01/SpringBoot-demo开源,供同学们一对一参考 Gitee传送门https://gitee.com/bugjun01/SpringBoot-demo,同时,原创开源不易,欢迎给个star🌟,想体验下被🌟的感jio,非常感谢❗
🫵 Who am I?
我是 bug菌:
- 热活跃于 CSDN:
https://blog.csdn.net/weixin_43970743| 掘金:https://juejin.cn/user/695333581765240| InfoQ:https://www.infoq.cn/profile/4F581734D60B28/publish| 51CTO:https://blog.51cto.com/u_15700751| 华为云:https://bbs.huaweicloud.cn/community/usersnew/id_1582617489455371| 阿里云:https://developer.aliyun.com/profile/uolxikq5k3gke| 腾讯云:https://cloud.tencent.com/developer/user/10216480/articles等技术社区; - CSDN 博客之星 Top30、华为云多年度十佳博主&卓越贡献奖、掘金多年度人气作者 Top40;
- 掘金、InfoQ、51CTO 等平台签约及优质作者;
- 全网粉丝累计 30w+。
更多高质量技术内容及成长资料,可查看这个合集入口 👉 点击查看:https://bbs.csdn.net/topics/612438251 👈️
硬核技术公众号 「猿圈奇妙屋」https://bbs.csdn.net/topics/612438251 期待你的加入,一起进阶、一起打怪升级。
- End -
- 点赞
- 收藏
- 关注作者
评论(0)