spring boot与ktor整合的实现方法

网友投稿 363 2022-11-24


spring boot与ktor整合的实现方法

背景

在用了一阵子 Ktor 之后,深感基于协程的方便,但是公司的主要技术栈是 SpringBoot,虽然已经整合了 Kotlin,但是如果有 Ktor 加持则会更加的方便。因此作了一番研究后,也完全可以实现这样的整合了。

建立一个 starter

首先新建一个 Kotlin 项目,在其 build.gradle 内加入对 SpringBoot 和 Ktor 的依赖,并同时加入对打为 jar 包的代码:

dependencies {

implementation "org.springframework.boot:spring-boot-starter-aop:${springBootVersion}"

implementation "io.ktor:ktor-jackson:${ktorVersion}"

compile "io.ktor:ktor-server-netty:${ktorVersion}"

compile "io.ktor:ktor-html-builder:${ktorVersion}"

testImplementation "org.springframework.boot:spring-boot-starter-test:${springBootVersion}"

testCompile "io.ktor:ktor-client-apache:${ktorVersion}"

testCompile "io.ktor:ktor-server-test-host:${ktorVersion}"

}

jar {

from {

configurations.runtime.collect { zipTree(it) }

}

}

task sourceJar(type: Jar) {

from sourceSets.main.allSource

classifier 'sources'

}

对于 SpringBoot 来说,工程内的 Configuration,Controller,Module 都是必要的,因此也需要 Ktor 可以符合这些约定俗成的组件。

那么就简单来实现一下吧,首先实现 Controller 的代码,我们只需要让 SpringBoot 的 Controller 支持 Ktor 的路由写法就可以了:

interface KRouter {

fun Routing.route()

}

@ContextDsl

fun Routing.request(

path: String,

body: PipelineInterceptor

) = route(path) { handle(body) }

然后实现基础的 Module:

interface KModule {

fun Application.defaultRegister(

useCompress: Boolean = false,

redirectHttps: Boolean = false,

headers: String = ""

) {

install(ContentNegotiation) { jackson { } }

install(PartialContent) { maxRangeCount = 10 }

if (useCompress) {

install(Compression) {

gzip { priority = 1.0 }

deflate {

priority = 10.0

minimumSize(1024)

}

}

}

if (redirectHttps) {

install(HttpsRedirect) {

sslPort = URLProtocol.HTTPS.defaultPort

permanentRedirect = true

}

}

if (headers != "") {

install(DefaultHeaders) {

headers.toCookieMap().forEach { (t, u) -> header(t, "$u") }

}

}

}

@ContextDsl

fun Application.register()

}

在这个 Module 内,defaultRegister 是通过读取 application.yml 内的配置的参数来决定的,register 是用来让用户覆盖,并实现额外的模块注册。

最后只需要实现 Configuration 就可以了,这里实现读取 yml 并且调用 defaultRegister 等方法:

/**

* spring.ktor 配置项

* @param host 服务器主机名

* @param port 绑定端口

* @param compress 是否启用压缩

* @param redirectHttps 是否自动重定向到 https

* @param headers 默认的请求头

*/

@ConfigurationProperties(prefix = "spring.ktor")

open class KProperties(

open var host: String = "0.0.0.0",

open var port: Int = 8080,

open var compress: Boolean = false,

open var redirectHttps: Boolean = false,

open var headers: String = ""

)

用这个类来映射 yml 内的配置,并且在取值后即可实现对模块,路由等的初始化:

@Configuration

@EnableConfigurationProperties(KProperties::class)

open class KConfiguration {

@Resource

private lateinit var properties: KProperties

@Bean

@ConditionalOnMissingBean

open fun engineFactory() = Netty

@Bean

@ConditionalOnMissingBean

open fun applicationEngine(

engineFactory: ApplicationEngineFactory,

context: ApplicationContext

): ApplicationEngine {

return embeddedServer(engineFactory, host = properties.host, port = properties.port) {

val modules = context.getBeansOfType(KModule::class.java).values

val routes = context.getBeansOfType(KRouter::class.java).values

modules.forEach { it.apply {

defaultRegister(

useCompress = properties.compress,

redirectHttps = properties.redirectHttps,

headers = properties.headers)

register()

} }

routing { routes.forEach { it.apply { route() } } }

}.start()

}

@Bean

@ConditionalOnMissingBean

open fun application(

applicationEngine: ApplicationEngine,

context: ApplicationContext

): Application = applicationEngine.environment.application

}

好了,一个简单的 starter 就完成了,最后加入一些配置就可以完成:

spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.isyscore.ktor.starter.configuration.KConfiguration

然后加入对配置项的描述:

additional-spring-configuration-metadata.json

{

"properties": [

{

"name": "spring.ktor.port",

"type": "java.lang.Integer",

"description": "服务启动时使用的端口号."

},

{

"name": "spring.ktor.host",

"type": "java.lang.String",

"description": "服务的主机IP或域名."

},

{

"name": "spring.ktor.compress",

"type": "java.lang.Boolean",

"description": "是否启用压缩."

},

{

"name": "spring.ktor.redirectHttps",

"type": "java.lang.Boolean",

"description": "是否自动重定向到 https."

},

{

"name": "spring.ktor.headers",

"type": "java.lang.String",

"description": "默认的请求头,以分号隔开."

}

]

}

最后我们只需要将这个 starter 发布到私有的 nexus 就完成了:

$ gradle publish

使用 starter

新建一个 SpringBoot 项目,并引入 starter:

implementation "com.rarnu:spring-boot-starter-ktor:0.0.1"

此时可以先在 yml 内加入配置项:

spring:

ktor:

port: 9000

compress: true

headers: X-Engine=Ktor

然后来实现 Configuration,Controller 和 Module:

TestConfiguration.kt

class TestConfiguration {

@Bean

fun engineFactory() = TestEngine

}

TestModule.kt

@Component

class TestModule : KModule {

override fun Application.register() {

// TODO: install custom plugins

}

}

TestController.kt

@Controller

class TestController : KRouter {

override fun Routing.route() {

request("/") {

call.respond(mapOf("code" to "001", "msg" to "操作成功。"))

}

get("/hello") {

call.respondText { "OK" }

}

}

}

完成后我们只需要写一个 Application,并且启动服务即可:

SpringKtorApplication.kt

@SpringBootApplication

open class SpringKtorApplication

fun main(args: Array) {

runApplication(*args)

}

现在就可以编译项目并且运行程序了:

$ gradle clean build

$ java -jar test-ktor.jar

总结

现在即可使用 Ktor 的写法来编写 SpringBoot 的路由了

可以使用 Ktor 协程

可以使用各种方便的 Ktor 插件

用上 Ktor 后,代码不麻烦了,心情也好了,效率更高了 :)


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:SpringBoot中使用Quartz管理定时任务的方法
下一篇:Spring Boot 通过CORS实现跨域问题
相关文章

 发表评论

暂时没有评论,来抢沙发吧~