开发环境
- idea 2019.3
- kotlin 1.3.41
- spring boot 2.1.6.RELEASE
- ktor 1.2.2
- ktorm 2.4
项目创建
使用idea创建kotlin项目,创建项目需使用gradle,如需使用maven创建项目请自行尝试,不要使用(https://start.spring.io/)创建kotlin项目除非能熟练使用kts脚本作为配置
项目配置
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.3.41'
id 'org.springframework.boot' version "2.1.6.RELEASE"
// ktolin对srping支持的插件
id "org.jetbrains.kotlin.plugin.spring" version "1.3.41"
}
apply plugin: 'io.spring.dependency-management'
dependencies {
compileOnly "org.springframework.boot:spring-boot-configuration-processor"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "org.springframework.boot:spring-boot-starter-aop"
implementation("org.springframework.boot:spring-boot-starter-jdbc")
implementation("me.liuwj.ktorm:ktorm-core:2.4")
implementation("me.liuwj.ktorm:ktorm-jackson:2.4")
implementation("mysql:mysql-connector-java:8.0.13")
implementation 'com.mchange:c3p0:0.9.5.4'
// ktor 核心库
compile "io.ktor:ktor-server-core:$ktor_version"
// 这里ktor使用的是 netty作为web容器,如需指定其它容器请参照官网
compile "io.ktor:ktor-server-netty:$ktor_version"
}
ktor封装
1.定义路由接口
interface KtorRouter {
/**
* 注册路由
*/
fun Route.router()
}
2.定义中间件接口
interface KtorMiddleware {
/**
* 注册中间件
*/
fun Application.middleware()
}
定义configuration
- Spring Boot会检查你发布的jar中是否存在META-INF/spring.factories文件,
- 该文件中以EnableAutoConfiguration为key的属性应该列出你的配置类,
- 如果在pring.factories中配置后可以不用再该配置类写@Configuration
class KtormConfiguration {
// 使用c3p0作为连接池,可自行更换
@Bean
@Qualifier(value = "dataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource")
fun dataSource(@Autowired environment: Environment): DataSource =
DataSourceBuilder.create().type(com.mchange.v2.c3p0.ComboPooledDataSource::class.java).build()
@Bean
fun database(dataSource: DataSource): Database {
return Database.connectWithSpringSupport(dataSource)
}
@Bean
fun ktormModule(): Module {
return KtormModule()
}
}
// 此处为web框架的加载,当ktor开始启动后将陷入死循环,所以需要先让spring boot加载其他配置项
@AutoConfigureAfter(name = arrayOf("org.fyc.snow.starter.conf.KtormConfiguration"))
@EnableConfigurationProperties(KtorProperties::class)
class KtorConfiguration {
@Resource
private lateinit var properties: KtorProperties
/**
* 注册ktor web容器,可自行更改
*/
@Bean
@ConditionalOnMissingBean
fun registerKtorWebContainer(): ApplicationEngineFactory<ApplicationEngine, out ApplicationEngine.Configuration> =
Netty
/**
* 注册引擎
* @param engineFactory 依赖引擎工厂
*/
@Bean
@ConditionalOnMissingBean
fun applicationEngine(
registerKtorWebContainer: ApplicationEngineFactory<ApplicationEngine, out ApplicationEngine.Configuration>,
context: ApplicationContext
): ApplicationEngine {
return embeddedServer(registerKtorWebContainer, host = properties.host, port = properties.port) {
val middlewares = context.getBeansOfType(KtorMiddleware::class.java).values
val routes = context.getBeansOfType(KtorRouter::class.java).values
// 注册模块
middlewares.forEach {
it.apply { middleware() }
}
// 注册路由
routing {
routes.forEach {
it.apply { router() }
}
}
}
}
/**
* 注册应用
* @param applicationEngine 依赖引擎
* @param context 依赖spring容器
*/
@Bean
@ConditionalOnMissingBean
fun application(applicationEngine: ApplicationEngine): Int {
applicationEngine.start(true)
return 0
}
/**
* ktor配置项
* @param host 服务器主机名
* @param port 绑定端口
*/
@ConfigurationProperties(prefix = "ktor")
open class KtorProperties(
var host: String = "0.0.0.0",
var port: Int = 8080
)
}
项目实例
1.控制器的使用测试
@Controller
class UserInfoController : KtorRouter {
override fun Route.router() {
route("/api") {
// 访问: /api/group
get("/group") {
testLog(call)
}
}
get("/add") {
val id = call.request.queryParameters["id"]?.toInt() ?: 0
val name = call.request.queryParameters["name"].toString()
val pwd = call.request.queryParameters["pwd"].toString()
val model = UserInfoModel {
this.id = id
this.name = name
this.pwd = pwd
}
call.respondText(UserInfoDao.add(model).toString())
}
get("/") {
call.info("@@@ ==== call log 这是测试! UserInfoController")
call.respondText("测试1111")
}
}
private fun testLog(call: ApplicationCall) {
// ...实现逻辑
}
}
2.中间件的使用测试
@Component
class KtorHttpintercept: KtorMiddleware {
override fun Application.middleware() {
// 拦截响应
install(StatusPages) {
// 配置500
exception<Throwable> { err ->
// 输出并写入到日志
call.error(getMessage(err))
call.respondText("500-${err.message}", contentType = ContentType.Text.Plain)
}
// 配置404
status(HttpStatusCode.NotFound) {
call.respondText("404-未找到指定路径", contentType = ContentType.Text.Plain)
}
}
install(ContentNegotiation) {
jackson {
enable(SerializationFeature.INDENT_OUTPUT)
}
}
}
// 获取异常全部信息
private fun getMessage(err: Throwable):String {
val strBuffer = StringBuffer("${err.message}\n")
for (se in err.stackTrace) {
strBuffer.append("\tat ${se.className}(${se.fileName}:${se.lineNumber})\n")
}
strBuffer.deleteCharAt(strBuffer.length -1)
strBuffer.append("}")
return strBuffer.toString()
}
}
3.启动项的使用测试
@SpringBootApplication(scanBasePackages = arrayOf("org.fyc"))
class Appclication
fun main(args: Array<String>) {
runApplication<Appclication>(*args)
}
总结
本项目旨在提供实践思路,亦可直接使用,如需使用请参考例子,关于ktorm的使用可参考项目中的使用,亦可扩展更多使用方法,更多使用方法请参考 ktorm,由于篇幅有限只能记录大概,当然如能熟练使用spring boot框架可自行按照例子的思路提供自己的封装。