spring boot與ktor整合的實(shí)現(xiàn)方法
背景
在用了一陣子 Ktor 之后,深感基于協(xié)程的方便,但是公司的主要技術(shù)棧是 SpringBoot,雖然已經(jīng)整合了 Kotlin,但是如果有 Ktor 加持則會(huì)更加的方便。因此作了一番研究后,也完全可以實(shí)現(xiàn)這樣的整合了。
建立一個(gè) starter
首先新建一個(gè) Kotlin 項(xiàng)目,在其 build.gradle 內(nèi)加入對(duì) SpringBoot 和 Ktor 的依賴,并同時(shí)加入對(duì)打?yàn)?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' }
對(duì)于 SpringBoot 來說,工程內(nèi)的 Configuration,Controller,Module 都是必要的,因此也需要 Ktor 可以符合這些約定俗成的組件。
那么就簡單來實(shí)現(xiàn)一下吧,首先實(shí)現(xiàn) Controller 的代碼,我們只需要讓 SpringBoot 的 Controller 支持 Ktor 的路由寫法就可以了:
interface KRouter { fun Routing.route() } @ContextDsl fun Routing.request( path: String, body: PipelineInterceptor<Unit, ApplicationCall> ) = route(path) { handle(body) }
然后實(shí)現(xiàn)基礎(chǔ)的 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() }
在這個(gè) Module 內(nèi),defaultRegister 是通過讀取 application.yml 內(nèi)的配置的參數(shù)來決定的,register 是用來讓用戶覆蓋,并實(shí)現(xiàn)額外的模塊注冊(cè)。
最后只需要實(shí)現(xiàn) Configuration 就可以了,這里實(shí)現(xiàn)讀取 yml 并且調(diào)用 defaultRegister 等方法:
/** * spring.ktor 配置項(xiàng) * @param host 服務(wù)器主機(jī)名 * @param port 綁定端口 * @param compress 是否啟用壓縮 * @param redirectHttps 是否自動(dòng)重定向到 https * @param headers 默認(rèn)的請(qǐng)求頭 */ @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 = "" )
用這個(gè)類來映射 yml 內(nèi)的配置,并且在取值后即可實(shí)現(xiàn)對(duì)模塊,路由等的初始化:
@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<ApplicationEngine, out ApplicationEngine.Configuration>, 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 }
好了,一個(gè)簡單的 starter 就完成了,最后加入一些配置就可以完成:
spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.isyscore.ktor.starter.configuration.KConfiguration
然后加入對(duì)配置項(xiàng)的描述:
additional-spring-configuration-metadata.json
{ "properties": [ { "name": "spring.ktor.port", "type": "java.lang.Integer", "description": "服務(wù)啟動(dòng)時(shí)使用的端口號(hào)." }, { "name": "spring.ktor.host", "type": "java.lang.String", "description": "服務(wù)的主機(jī)IP或域名." }, { "name": "spring.ktor.compress", "type": "java.lang.Boolean", "description": "是否啟用壓縮." }, { "name": "spring.ktor.redirectHttps", "type": "java.lang.Boolean", "description": "是否自動(dòng)重定向到 https." }, { "name": "spring.ktor.headers", "type": "java.lang.String", "description": "默認(rèn)的請(qǐng)求頭,以分號(hào)隔開." } ] }
最后我們只需要將這個(gè) starter 發(fā)布到私有的 nexus 就完成了:
$ gradle publish
使用 starter
新建一個(gè) SpringBoot 項(xiàng)目,并引入 starter:
implementation "com.rarnu:spring-boot-starter-ktor:0.0.1"
此時(shí)可以先在 yml 內(nèi)加入配置項(xiàng):
spring: ktor: port: 9000 compress: true headers: X-Engine=Ktor
然后來實(shí)現(xiàn) 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" } } } }
完成后我們只需要寫一個(gè) Application,并且啟動(dòng)服務(wù)即可:
SpringKtorApplication.kt
@SpringBootApplication open class SpringKtorApplication fun main(args: Array<String>) { runApplication<SpringKtorApplication>(*args) }
現(xiàn)在就可以編譯項(xiàng)目并且運(yùn)行程序了:
$ gradle clean build $ java -jar test-ktor.jar
總結(jié)
現(xiàn)在即可使用 Ktor 的寫法來編寫 SpringBoot 的路由了
可以使用 Ktor 協(xié)程
可以使用各種方便的 Ktor 插件
用上 Ktor 后,代碼不麻煩了,心情也好了,效率更高了 :)
到此這篇關(guān)于spring boot與ktor整合的文章就介紹到這了,更多相關(guān)spring boot與ktor整合內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java?Chassis3的多種序列化方式支持技術(shù)解密
這篇文章主要為大家介紹了Java?Chassis?3多種序列化方式支持技術(shù)解密,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01SpringBoot中maven項(xiàng)目打成war包部署在linux服務(wù)器上的方法
這篇文章主要介紹了SpringBoot中maven項(xiàng)目打成war包部署在linux服務(wù)器上的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05JVM調(diào)優(yōu)參數(shù)的設(shè)置
Java虛擬機(jī)的調(diào)優(yōu)是一個(gè)復(fù)雜而關(guān)鍵的任務(wù),可以通過多種參數(shù)來實(shí)現(xiàn),本文就來介紹一下JVM調(diào)優(yōu)參數(shù)的設(shè)置,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03Spring Boot 如何使用Liquibase 進(jìn)行數(shù)據(jù)庫遷移(操作方法)
在Spring Boot應(yīng)用程序中使用Liquibase進(jìn)行數(shù)據(jù)庫遷移是一種強(qiáng)大的方式來管理數(shù)據(jù)庫模式的變化,本文重點(diǎn)講解如何在Spring Boot應(yīng)用程序中使用Liquibase進(jìn)行數(shù)據(jù)庫遷移,從而更好地管理數(shù)據(jù)庫模式的變化,感興趣的朋友跟隨小編一起看看吧2023-09-09SpringBoot JSON全局日期格式轉(zhuǎn)換器實(shí)現(xiàn)方式
這篇文章主要介紹了SpringBoot JSON全局日期格式轉(zhuǎn)換器,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04Java求一個(gè)分?jǐn)?shù)數(shù)列的前20項(xiàng)之和的實(shí)現(xiàn)代碼
這篇文章主要介紹了Java求一個(gè)分?jǐn)?shù)數(shù)列的前20項(xiàng)之和的實(shí)現(xiàn)代碼,需要的朋友可以參考下2017-02-02