以下是在使用Kotlin进行服务端开发以来所遇到的问题记录。
1. fastjson不能正确的对数据类(data class)进行序列化及反序列化。
首先先来看下相关的代码:
data class User(
val id: Long?,
val username: String
)
如上的一个实体类,在使用fastjson进行序列化时,会报找不到无参构造方法的错误。
原因:
Kotlin的语法中User之后的大括号表示User类的主构造方法,同时data修饰的class必须在主构造方法中定义所有的属性字段,因此编译过后只会存在指定参数的构造方法。
解决方法:
- 修改数据类的定义,对每个属性字段设置默认值(不推荐)
data class User(
val id: Long? = null,
val username: String = ""
)
利用Kotlin通过支持参数默认值来实现方法重载的特性使该类在进行序列化时可以找到重载的无参构造方法。
- 使用1.2.36+版本的fastjson(推荐)
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.36</version>
</dependency>
- Kotlin编译插件中开启noarg的支持(推荐)
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<version>${kotlin.version}</version>
<configuration>
<compilerPlugins>
<plugin>no-arg</plugin>
</compilerPlugins>
<jvmTarget>1.8</jvmTarget>
<experimentalCoroutines>enable</experimentalCoroutines>
</configuration>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-noarg</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
2. Mybatis/JPA中实体类使用数据类(data class)定义时,会报找不到无参构造方法的错误。
原因:
同上一个问题。
解决方法:
- 修改数据类的定义,对每个属性字段设置默认值(不推荐)
data class User(
val id: Long? = null,
val username: String = ""
)
- Kotlin编译插件中开启noarg的支持(推荐)
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<version>${kotlin.version}</version>
<configuration>
<compilerPlugins>
<plugin>no-arg</plugin>
</compilerPlugins>
<jvmTarget>1.8</jvmTarget>
<experimentalCoroutines>enable</experimentalCoroutines>
</configuration>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-noarg</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
3. SpringMVC中validator的注解不生效的问题。
通常在Java Web应用中,使用SpringMVC支持的参数验证器的步骤为:
//DTO
public class LoginDTO {
@Size(min=6, max=12, message="{username.condition}")
private String username;
@Size(min=6, max=12, message="{password.condition}")
private String password;
// getter and setter
}
// Request Handle Method
...
public ResponseEntity login(@Valiadted LoginDTO loginDTO,
BindingResult result) {
}
...
在表现层的数据传输对象中使用validtor相关的约束注解标注字段的校验条件,同时在controller的请求处理方法参数上使用@Validated注解标注要进行校验的参数,这样在配置好校验器之后,SpringMVC就能自动的在请求参数解析时进行参数校验,从而提高请求处理的健壮性。
但将上述代码转换成Kotlin后, 其数据转换对象如下:
data class LoginDTO(
val username: String,
val password: String
)
这时使用validator的约束注解进行标注如下:
data class LoginDTO(
@Size(min=6, max=12, message="{username.condition}")
val username: String,
@Size(min=6, max=12, message="{password.condition}")
val password: String
)
再启动程序进行请求,会发现参数校验失效。通过断点调试会发现,在进行校验时,校验器无法获取到字段上的约束集合,从而导致参数校验的失效。
原因:
在Java中validator的约束注解实际上是作用于属性的getter方法,而在Kotlin的数据类中所有的属性均为不可变属性,且必须在初始化时指定或在构造方法中设置默认值,因此数据类作为一个安全的数据承载对象,可以直接使用.属性名
的方式去访问属性的值,因而也不存在getter方法,最终导致了约束注解的失效。
解决方法:
使用@field:修饰约束注解,可使之生效
data class LoginDTO(
@field:Size(min=6, max=12, message="{username.condition}")
val username: String,
@field:Size(min=6, max=12, message="{password.condition}")
val password: String,
)
4. Spring Bean注入的问题。
通常我们在Java Web应用中使用Spring时,最常用的两种注入方式1. 构造器注入;2. 属性值注入
,例如:
@Service
public class UserServcieImpl implements UserService {
@Autowired
private UserRepo userRepo;
private MessageSource messageSource;
public UserServiceImpl(MessageSource messageSource) {
this.messgeSource = messageSource
}
}
而在Kotlin中,由于类的定义方式的改变,通常更倾向于使用构造器注入的方式,如:
@Service
class UserServiceImpl(
val userRepo: UserRepo,
val messageSource: MessageSource
): UserService
同时Kotlin中也支持属性值注入:
@Service
class userServiceImpl: UserService {
@Autowired
lateinit var userRepo: UserRepo
}
但是在使用Kotlin编写的Junit单元测试类中,将只能使用属性值注入的方式:
class UserServiceTest : ServiceTest() {
@Autowired
lateinit var userRepo: UserRepo
@Test
fun baseJPAQueryTest() {}
因为Junit测试类要求,测试类不能有含参的构造方法。
未完待续。。。
PS:
- 感谢您的阅读,希望我的记录对您有所帮助。
- 欢迎各种提问,在相互探讨中共同提高。
- 如果筒子们有遇到过其他的问题,也欢迎留言给我提供研究的素材。
网友评论