前段时间用Kotlin完成一个SpringBoot小项目时遇到了些问题,写一篇以记录.
代码地址:https://github.com/clockknock/lottery
1.data class empty constructor
Kotlin里面,如果使用了data class后,我们在Controller想要接收一个对象参数的时候,应用程序会报错,会说找不到空参构造,但如果我又还是想用data class那该怎么办呢?
@PostMapping("/")
fun save(user: User): String {//这行会提示没有可用的构造函数,因为我们知道Spring是通过该对象字节码获取空参构造来创建对象并调用setter来给field赋值的
user.uid = UUIDUtil.getId()
user.password = MD5Util.encodeSalt(user.password!!, salt)
userService.save(user)
return "redirect:loginUI"
}
我们如果是通过IDEA的SpringInitializr创建的基于gradle的kotlin项目,那build.gradle中一般是如下所示:
buildscript {
...
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}")
}
}
apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
...
我们只需要添加两行代码就能让Kotlin的data class拥有空参构造
buildscript {
...
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}")
//===================这一行===================
classpath("org.jetbrains.kotlin:kotlin-noarg:${kotlinVersion}")
}
}
apply plugin: 'kotlin'
//===================以及这一行===================
apply plugin: "kotlin-jpa"
apply plugin: 'kotlin-spring'
...
这时我们再回到我们的程序调用相应的方法想Spring自己给我们提供data class对象,就不会再有问题了
2.Runtime Annotation With Delegate
写的小项目中有这么一个问题,数据库中有一个表中的字段是以字符串形式存储的例如:"01,05,13,17,26,32",而在前端页面展示时需要将其以逗号为分隔符一个一个展示出来,我们得取出来后每次都split一下,很是麻烦,而且也不好直接去修改数据库的数据存储形式,只好在别的地方动手脚了
runtime01.png在Kotlin中我们的field可以委托出去,我在BallHistory中声明了一个reds字段委托给了String2List这个类以处理原始字段red
@Entity
@Table(name = "ballhistory")
class BallHistory {
...
//原始数据库字段
var red: String? = null
//想要的可以遍历的处理后字段,写了by String2List后,会自动生成相应的委托方法
var reds: List<String>? by String2List
...
}
String2List:
object String2List {
//有了这个委托方法后,我们的BallHistory.reds拿到的就已经是List<String>?类型的对象了
operator fun getValue(ballHistory: BallHistory, property: KProperty<*>): List<String>? {
//将ballHistory对象的red以","分隔返回一个list
return ballHistory.red?.split(",")
}
//set方法并不需要如何做,如果有需要的话,可以把list转回String赋值给ballHistory的red
operator fun setValue(ballHistory: BallHistory, property: KProperty<*>, list: List<String>?) {
}
}
在做了以上处理后,reds这个field会被JPA识别成一个需要持久化到数据库的字段,这不是我想要的结果,我只想要reds这个list为我的业务提供一些帮助,JPA有提供一个注解@Transient,可以让这个字段不被持久化,但我们这个是委托属性,@Transient注解是运行时注解,无法帮我们做事情
runtime02.png在文章底部的参考中有看到一个kotlin的注解可以帮我们将委托字段也可被运行时注解(Runtime Annotation)适用
@delegate:Transient
var reds: List<String>? by String2List
用了@delegate注释后,就可以让@Transient这个运行时注解生效了
参考:
https://kotlinlang.org/docs/reference/annotations.html#annotation-use-site-targets
网友评论