LOADING 0
arrow_back

post-title

kotlin + spring boot 11/12/2018

kotlin + spring boot

初始化项目

  • jdk 1.8
  • kotlin 1.3
  • spring boot 2.1.0
  • gradle 4.4
    Image text

添加allopen, noarg插件

build.gradle

1
2
3
4
5
6
7
8
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}")
+++
}

新增Poko注解类

Poko.kt

1
2
3
package com.example.ktspringboot1112.annotation

annotation class Poko

声明plugin

build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
+++
apply plugin: 'kotlin-allopen'
apply plugin: 'kotlin-noarg'

allOpen {
annotation("com.example.ktspringboot1112.annotation.Poko")
}
noArg {
annotation("com.example.ktspringboot1112.annotation.Poko")
}
+++

创建User bean

User.kt

1
2
3
4
5
6
7
8
package com.example.ktspringboot1112.bean

import com.example.ktspringboot1112.annotation.Poko
// 在kotlin的数据类上声明@Poko注解
// allopen: 生成非final类型的可继承类
// noarg: 生成无参的构造方法
@Poko
data class User(var id: Int, var username: String, var password: String)

创建 UserMapper

UserMapper.kt

1
2
3
4
5
6
7
8
9
package com.example.ktspringboot1112.mapper

import com.example.ktspringboot1112.bean.User
import org.apache.ibatis.annotations.Mapper

@Mapper
interface UserMapper {
fun getAllUser(): List<User>
}

创建mybatis-config.xml

resources/mybatis/mybatis-config.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

<settings>
<!-- 开启全局二级缓存 -->
<setting name="cacheEnabled" value="true"/>
<!-- last_name转lastName-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 分步查询时懒加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>

</configuration>

创建user.xml

resources/mybatis/mapper/user.xml

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--名称空间namespace指向接口UserMapper-->
<mapper namespace="com.example.ktspringboot1112.mapper.UserMapper">

<select id="getAllUser" resultType="com.example.ktspringboot1112.bean.User">
SELECT * FROM users
</select>

</mapper>

创建UserService

UserService.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.example.ktspringboot1112.service

@Service
class UserService {
val logger: Logger = LoggerFactory.getLogger(javaClass)

@Autowired
lateinit var userMapper: UserMapper

fun getAllUser(): List<User> {
logger.info("service -> getAllUser")

val list = userMapper.getAllUser()

return list
}
}

创建UserController

UserController.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.example.ktspringboot1112.controllers

@RestController
class UserController {
val logger: Logger = LoggerFactory.getLogger(javaClass)

@Autowired
lateinit var userService: UserService

@GetMapping("/users")
fun getAllUser(): List<User> {
logger.info("controller -> getAllUser")

val list = userService.getAllUser()

return list
}
}

application配置

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server:
port: 3000

spring:
datasource:
username: root
password: 233233
url: jdbc:mysql://localhost:3306/ktsdb?useSSL=false&serverTimezone=UTC
driver-class-name: com.mysql.jdbc.Driver

mybatis:
mapper-locations: classpath:mybatis/mapper/*.xml
config-location: classpath:mybatis/mybatis-config.xml

logging:
level:
com.example.ktspringboot1112.mapper: debug

数据库

创建 users 表
Image text

启动

启动spring boot, 访问 http://127.0.0.1:3000/users

Image text

添加拦截器

InterceptorConfig.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.example.ktspringboot1112.config

/**
* 自定义拦截器
*/
class InterceptorConfig: HandlerInterceptor {
val logger = LoggerFactory.getLogger(javaClass)

override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
logger.info("开始进入请求地址拦截: url -> ${request.requestURL} ----------------------------")
val session: HttpSession = request.session

if (session.getAttribute("uid") == null) {
// 这句话的意思,是让浏览器用utf8来解析返回的数据
response.characterEncoding = "UTF-8"
// 这句话的意思,是告诉servlet用UTF-8转码,而不是用默认的ISO8859
response.setHeader("Content-Type", "text/html;charset=UTF-8")

val map = HashMap<String, Any?>()
map.put("code", 0)
map.put("message", "请登陆")
response.writer.write(map.toString())

return false
} else {
return true
}
}

override fun postHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any, modelAndView: ModelAndView?) {
logger.info("---------------------处理请求完成后视图渲染之前的处理操作----------------------------")

}

override fun afterCompletion(request: HttpServletRequest, response: HttpServletResponse, handler: Any, ex: Exception?) {
logger.info("---------------------视图渲染之后的操作----------------------------")
}
}

WebAppConfig.kt

1
2
3
4
5
6
7
8
9
package com.example.ktspringboot1112.config

@Configuration
class WebAppConfig: WebMvcConfigurerAdapter() {
override fun addInterceptors(registry: InterceptorRegistry) {
// 这里可以添加多个拦截器
registry.addInterceptor(InterceptorConfig()).addPathPatterns("/auth1", "/auth2")
}
}

UserController.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.example.ktspringboot1112.controllers

@RestController
class UserController {
val logger: Logger = LoggerFactory.getLogger(javaClass)

@Autowired
lateinit var userMapper: UserMapper

@Autowired
lateinit var userService: UserService

@GetMapping("/users")
fun getAllUser(): List<User> {
logger.info("controller -> getAllUser")
val list = userService.getAllUser()
return list
}

@GetMapping("/auth1")
fun auth1(): String {
return "auth1"
}

@GetMapping("/auth2")
fun auth2(): String {
return "auth2"
}
}

访问 http://127.0.0.1:3000/auth2

Image text

validation验证参数

UserMapper.kt

1
2
3
4
5
6
7
8
9
10
11
12
package com.example.ktspringboot1112.mapper

import com.example.ktspringboot1112.bean.User
import org.apache.ibatis.annotations.Mapper

@Mapper
interface UserMapper {
fun getAllUser(): List<User>
+++
fun findUserByNameAndPass(user: User): List<User>
+++
}

user.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--名称空间namespace指向接口EmployeeMapper-->
<mapper namespace="com.example.ktspringboot1112.mapper.UserMapper">

<select id="getAllUser" resultType="com.example.ktspringboot1112.bean.User">
SELECT * FROM users
</select>
+++
<select id="findUserByNameAndPass" resultType="com.example.ktspringboot1112.bean.User">
SELECT * FROM users WHERE username = #{username} AND password = #{password}
</select>
+++
</mapper>

UserService.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.example.ktspringboot1112.service

import com.example.ktspringboot1112.bean.User
import com.example.ktspringboot1112.mapper.UserMapper
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service

@Service
class UserService {
@Autowired
lateinit var userMapper: UserMapper
val logger: Logger = LoggerFactory.getLogger(javaClass)

/**
* 查找全部用户
* @return List<User>
*/
fun getAllUser(): List<User> {
logger.info("SERVICE -> getAllUser")
val list = userMapper.getAllUser()

return list
}
+++
/**
* 根据用户名和密码查找用户
* @param user User
* @return List<User>
*/
fun findUserByNameAndPass(user: User): List<User> {
logger.info("SERVICE -> findUserByNameAndPass")
val list = userMapper.findUserByNameAndPass(user)

return list
}
+++
}

User.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.example.ktspringboot1112.bean

/**
* field标识符只允许在属性的访问器函数内使用。它能够 validation 校验注解作用与属性
*/
// 声明@Poko注解
@Poko
data class User(
var id: Int?,
@field:Size(min = 6, max = 12, message = "用户名在6-12字符之间")
@field:NotEmpty(message = "用户名不允许为空")
var username: String,
@field:NotEmpty(message = "密码不能为空")
@field:Size(min = 6, max = 12, message = "密码在6-12字符之间")
var password: String)

UserController.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
+++
/**
* 在需要验证的方法参数上声明@Valid注解
*/
@PostMapping("/login")
fun login(@Valid user: User, request: HttpServletRequest): HashMap<String, Any?> {
logger.info("CONTROLLER -> login")
val list = userService.findUserByNameAndPass(user)
logger.info("CONTROLLER -> 获取到请求登陆的用户数据: ${list}")
val map = HashMap<String, Any?>()
if (list.size == 0) {
map.put("message", "用户名或密码错误")
} else {
map.put("message", "登陆成功")
val session = request.session
val user = list.get(0)
session.setAttribute("uid", user.id)
session.setAttribute("username", user.username)
logger.info("登陆成功, 设置 uid: ${user.id}, username: ${user.username}")
}

return map
}
+++

在验证参数失败时, 会抛出 BindException 异常, 定义捕获 BindException 异常的类
BindExceptionHandler.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.example.ktspringboot1112.exception

/**
* 全局处理 BindException 异常
*/
// 声明@ControllerAdvice注解
@ControllerAdvice
class BindExceptionHandler {
val logger = LoggerFactory.getLogger(javaClass)

@ExceptionHandler(BindException::class)
@ResponseBody
fun handler(ex: BindException): Map<String, Any?> {
val fieldError: FieldError? = ex.getFieldError()
val message = fieldError?.defaultMessage

logger.error("BindExceptionHandler: 捕获到controller错误 -> ${message}")
val map = HashMap<String, Any?>()
map.put("message", message)
map.put("code", 0)

return map
}
}

设置validator为快速返回模式
ValidatorConfig.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.example.ktspringboot1112.config

@Configuration
class ValidatorConfig {
/**
* 两种验证模式返回模式
* ailFast(true)
* addProperty("hibernate.validator.fail_fast", "true")
*/
@Bean
fun validator(): Validator {
val validatorFactory = Validation.byProvider(HibernateValidator::class.java)
.configure()
.failFast(true) // 快速失败返回模式(只要有一个验证失败,则返回) false 普通模式(会校验完所有的属性,然后返回所有的验证失败信息)
// .addProperty("hibernate.validator.fail_fast", "true")
.buildValidatorFactory()

val validator: Validator = validatorFactory.validator

return validator
}

@Bean
fun methodValidationPostProcessor(): MethodValidationPostProcessor {
val processor = MethodValidationPostProcessor()

processor.setValidator(validator())

return processor
}
}

post请求 /login 接口

Image text

Image text

验证密码的情况

Image text

Image text