文章目录
- 前言
- 一、项目示例代码
- 1.build.gradle
- 2.gradle.properties
- 3.application.yml
- 4.Application.kt
- 5.core/api/KtorPlugin
- 6.core/api/KtorRouter
- 7.core/api/configuration
- 8.configuration/MybatisPlus
- 9.field/entity/UserDo
- 10.field/dao/mapper/IUserMapper
- 11.field/handler/MyMetaObjectHandler
- 12.field/service/IStudentService
- 13.field/service/impl/StudentServiceImpl
- 14.plugin/Jackson
- 15.controller/BaseController
- 16.controller/Hello
- 总结
前言
最近对sprinp data jpa与mybatis-plus做了对比研究,基于流行度与灵活性而言选择了mybatis-plus,jpa个人感觉也还不错不过对于人员要求可能稍微高一点,本篇文章主要记录使用springboot整合ktor的使用,maven做为项目构建很好但是不如gradle简单,由于开发采用kotlin做为开发语言所以使用的技术栈为:springboot + ktor + mybatis-plus + kotlin + gradle,之前打算使用koin替代spring的,但是mybatis-plus与springboot整合比较方便就懒得选择了。
一、项目示例代码
1.build.gradle
plugins {
id 'org.springframework.boot' version "$spring_version"
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
// id 'java'
id 'org.jetbrains.kotlin.jvm' version "$kotlin_version"
id "org.jetbrains.kotlin.plugin.spring" version "$kotlin_version"
id "org.jetbrains.kotlin.plugin.noarg" version "$kotlin_version"
id "org.jetbrains.kotlin.plugin.allopen" version "$kotlin_version"
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
noArg {
// annotation("org.xunlongkj.fyc.annotation.KtNoArg")
annotation("com.baomidou.mybatisplus.annotation.TableName")
}
allOpen {
// annotation("org.xunlongkj.fyc.annotation.KtAllOpen")
}
repositories {
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/central' }
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
maven { url 'https://maven.aliyun.com/repository/spring' }
maven { url 'https://maven.aliyun.com/repository/spring-plugin' }
maven { url "https://maven.pkg.jetbrains.space/public/p/ktor/eap" }
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// ktor
implementation "io.ktor:ktor-server-core-jvm:$ktor_version"
implementation "io.ktor:ktor-server-content-negotiation-jvm:$ktor_version"
implementation "io.ktor:ktor-serialization-jackson-jvm:$ktor_version"
implementation "io.ktor:ktor-server-netty-jvm:$ktor_version"
// kotlin
implementation 'com.fasterxml.jackson.module:jackson-module-kotlin'
implementation 'org.jetbrains.kotlin:kotlin-reflect'
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
//
// mybatis
implementation "com.baomidou:mybatis-plus-boot-starter:$mybatis_version"
implementation "com.baomidou:mybatis-plus-generator:$mybatis_version" // 代码生成器
implementation "org.apache.velocity:velocity:$velocity_version" // 代码生成器需要的默认模板引擎
//mysql数据库依赖
runtimeOnly 'mysql:mysql-connector-java'
implementation "com.alibaba:druid-spring-boot-starter:$druid_spring_version"
}
tasks.withType(JavaCompile) {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
bootJar {
archiveBaseName.set("demo")
archiveVersion.set("1.0.0")
}
jar {
enabled = true
}
2.gradle.properties
用于版本管理,gradle会默认加载‘gradle.properties的配置’(示例):
ktor_version=2.0.2
kotlin_version=1.7.0
spring_version=2.7.0
mybatis_version=3.5.2
velocity_version=1.7
druid_spring_version=1.2.11
3.application.yml
程序配置文件
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/fastapi_t1
username: admin
password: admin2022
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
global-config:
db-config:
# 主键类型 AUTO:"数据库ID自增" INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID"
id-type: auto
# 表名是否使用驼峰转下划线命名,只对表名生效
table-underline: false
logic-not-delete-value: 0
logic-delete-value: 1
configuration:
# 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射。
map-underscore-to-camel-case: false
# 打印日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
4.Application.kt
程序入口文件
package com.example.springbootktor1
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class Application
fun main(args: Array<String>) {
runApplication<Application>(*args)
// initDbConf()
}
// 使用mybatis-plus 自动生成代码
// 需要的依赖 ['com.baomidou:mybatis-plus-generator', 'org.apache.velocity:velocity']
//fun initDbConf() {
// val path = System.getProperty("user.dir")
//
// FastAutoGenerator.create("jdbc:mysql://127.0.0.1:3306/fastapi_t1", "admin", "admin2022")
// .globalConfig { globalConfig ->
// globalConfig.outputDir("$path/src/main/java") // 指定生成路径, 由于只能生成java数据, 所以暂时生成在java目录下
// globalConfig.disableOpenDir() // 不打开输出目录
// globalConfig.author("fyc") // 设置作者
// }
// .packageConfig { packageConfig-> // 配置包相关的信息
// packageConfig.parent("com.xunlongkj.fyc.ssmt1") // 设置父包名
// packageConfig.moduleName("mybatisPlus") // 设置父包模块名
// }
// .strategyConfig { strategyConfig-> // 配置策略
// strategyConfig.entityBuilder()
// .naming(NamingStrategy.underline_to_camel) // 数据库表名映射成java类的名称
// .columnNaming(NamingStrategy.underline_to_camel) // 数据库字段映射成java 成员变量的名称
// .enableTableFieldAnnotation() // 开启生成实体时生成字段注解
// .enableLombok() // 开启lombok注解
// }
// .execute() // 执行
//}
5.core/api/KtorPlugin
package com.example.springbootktor1.core.api
import io.ktor.server.application.*
interface KtorPlugin {
/**
* 注册中间件
*/
fun Application.regPlugin()
}
6.core/api/KtorRouter
package com.example.springbootktor1.core.api
import io.ktor.server.routing.*
interface KtorRouter {
/**
* 注册路由
*/
fun Route.regRouter()
}
7.core/api/configuration
package com.example.springbootktor1.core.configuration
import com.example.springbootktor1.core.api.KtorPlugin
import com.example.springbootktor1.core.api.KtorRouter
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.routing.*
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class Ktor {
@Autowired
private lateinit var applicationContext: ApplicationContext
@Bean
fun ktorEngine() {
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
val plugins = applicationContext.getBeansOfType(KtorPlugin::class.java).values
val routes = applicationContext.getBeansOfType(KtorRouter::class.java).values
// 注册模块
plugins.forEach {
it.apply { regPlugin() }
}
// 注册路由
routing {
routes.forEach {
it.apply { regRouter() }
}
}
}.start(wait = true)
}
}
8.configuration/MybatisPlus
package com.example.springbootktor1.configuration
import com.baomidou.mybatisplus.annotation.DbType
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor
import org.mybatis.spring.annotation.MapperScan
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.transaction.annotation.EnableTransactionManagement
// MybatisPlus 配置注入springboot
@Configuration
@EnableTransactionManagement
@MapperScan("com.example.springbootktor1.**.dao.mapper") // 注入mybatis-plus到spring容器中
class MybatisPlus {
/**
* mybatis-plus分页插件, 老版本已经失效
*/
@Bean
fun paginationInnerInterceptor(): MybatisPlusInterceptor {
val interceptor = MybatisPlusInterceptor()
interceptor.addInnerInterceptor(PaginationInnerInterceptor(DbType.MYSQL));
return interceptor
}
/**
* 乐观锁mybatis插件
*/
@Bean
fun optimisticLockerInnerInterceptor(): MybatisPlusInterceptor {
val interceptor = MybatisPlusInterceptor()
interceptor.addInnerInterceptor(OptimisticLockerInnerInterceptor())
return interceptor
}
}
9.field/entity/UserDo
package com.example.springbootktor1.field.entity
import com.baomidou.mybatisplus.annotation.*
import java.util.*
enum class EUser(
@EnumValue
val code: Int,
val msg: String
)
{
REST(0, "休息"),
WORK(1, "上班"),
}
@TableName("student")
data class UserDo(
@TableId
var id: Long?,
var name: String?,
var age: Int?,
@TableField("is_delete")
var isDel: Boolean = false,
@TableField("create_time", fill = FieldFill.INSERT)
var createAt: Date?,
@TableField("update_time", fill = FieldFill.INSERT_UPDATE)
var updateAt: Date?,
@Version
var version: Int?,
var status: EUser = EUser.WORK,
@TableLogic
var deleted: Int = 0,
) {
companion object {
fun new(name: String, age: Int): UserDo {
return UserDo(null, name, age, false, null, null, null)
}
}
}
10.field/dao/mapper/IUserMapper
package com.example.springbootktor1.field.dao.mapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper
import com.example.springbootktor1.field.entity.UserDo
interface IUserMapper: BaseMapper<UserDo>
11.field/handler/MyMetaObjectHandler
package com.example.springbootktor1.field.handler
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler
import org.apache.ibatis.reflection.MetaObject
import org.springframework.stereotype.Component
import java.util.Date
// 处理时间自动插入, 并注入到springboot中
@Component
class MyMetaObjectHandler: MetaObjectHandler {
override fun insertFill(metaObject: MetaObject?) {
this.setFieldValByName("createAt", Date(), metaObject)
this.setFieldValByName("updateAt", Date(), metaObject)
}
override fun updateFill(metaObject: MetaObject?) {
this.setFieldValByName("updateAt", Date(), metaObject)
}
}
12.field/service/IStudentService
package com.example.springbootktor1.field.service
import com.baomidou.mybatisplus.extension.service.IService
import com.example.springbootktor1.field.entity.UserDo
interface IStudentService: IService<UserDo>
13.field/service/impl/StudentServiceImpl
package com.example.springbootktor1.field.service.impl
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl
import com.example.springbootktor1.field.dao.mapper.IUserMapper
import com.example.springbootktor1.field.entity.UserDo
import com.example.springbootktor1.field.service.IStudentService
import org.springframework.stereotype.Service
@Service
class StudentServiceImpl: ServiceImpl<IUserMapper, UserDo>(), IStudentService
14.plugin/Jackson
package com.example.springbootktor1.plugin
import com.example.springbootktor1.core.api.KtorPlugin
import com.fasterxml.jackson.databind.SerializationFeature
import io.ktor.serialization.jackson.*
import io.ktor.server.application.*
import io.ktor.server.plugins.contentnegotiation.*
import org.springframework.stereotype.Component
@Component
class Jackson: KtorPlugin {
override fun Application.regPlugin() {
install(ContentNegotiation) {
jackson {
enable(SerializationFeature.INDENT_OUTPUT)
}
}
}
}
15.controller/BaseController
package com.example.springbootktor1.controller
import com.example.springbootktor1.core.api.KtorRouter
abstract class BaseController: KtorRouter
16.controller/Hello
package com.example.springbootktor1.controller
import com.example.springbootktor1.field.service.IStudentService
import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Controller
@Controller
class Hello: BaseController() {
@Autowired
private lateinit var studentService: IStudentService
override fun Route.regRouter() {
get("/") {
call.respond(studentService.list())
}
}
}
总结
该示例只是用来演示使用ktor替代spring mvc的可能性,当然ktor本质上也可以用于实际生产项目,这得看对ktor的了解程度,ktor官方文档写的还算详细,本例子只是对ktor做了简单的封装,当然也加快了springboot的运行速度。