这篇文章将为大家详细讲解有关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 { zipTreeit) } } } task sourceJartype: 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<Unit, ApplicationCall> ) = routepath) { handlebody) }
然后实现基础的 Module:
interface KModule { fun Application.defaultRegister useCompress: Boolean = false, redirectHttps: Boolean = false, headers: String = "" ) { installContentNegotiation) { jackson { } } installPartialContent) { maxRangeCount = 10 } if useCompress) { installCompression) { gzip { priority = 1.0 } deflate { priority = 10.0 minimumSize1024) } } } if redirectHttps) { installHttpsRedirect) { sslPort = URLProtocol.HTTPS.defaultPort permanentRedirect = true } } if headers != "") { installDefaultHeaders) { headers.toCookieMap).forEach { t, u) -> headert, "$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 默认的请求头 */ @ConfigurationPropertiesprefix = "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 @EnableConfigurationPropertiesKProperties::class) open class KConfiguration { @Resource private lateinit var properties: KProperties @Bean @ConditionalOnMissingBean open fun engineFactory) = Netty @Bean @ConditionalOnMissingBean open fun applicationEngine engineFactory: ApplicationEngineFactory<ApplicationEngine, out ApplicationEngine.Configuration>, context: ApplicationContext ): ApplicationEngine { return embeddedServerengineFactory, host = properties.host, port = properties.port) { val modules = context.getBeansOfTypeKModule::class.java).values val routes = context.getBeansOfTypeKRouter::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.respondmapOf"code" to "001", "msg" to "操作成功。")) } get"/hello") { call.respondText { "OK" } } } }
完成后我们只需要写一个 Application,并且启动服务即可:
SpringKtorApplication.kt
@SpringBootApplication open class SpringKtorApplication fun mainargs: Array<String>) { runApplication<SpringKtorApplication>*args) }
现在就可以编译项目并且运行程序了:
$ gradle clean build $ java -jar test-ktor.jar