Spring2Ktor Server Processor
自动收集自
lib/ksp/metadata/spring2ktor-server-processor/README.md。
用 Spring 原生 Web 注解写接口,在编译期生成干净的 Ktor Server 路由注册代码。
它不是 Spring Boot,也不会启动 Spring 容器;它只是读取 Spring 注解,然后生成 Ktor 代码。
适用场景
- 想保留
@GetMapping、@PostMapping、@RequestBody这一套写法 - 运行时想用 Ktor,而不是 Spring MVC / Spring Boot
- 主要写 Kotlin 顶层函数,同时希望兼容少量
@RestController
Gradle 接入
推荐直接使用消费插件:
plugins {
kotlin("jvm")
id("site.addzero.ksp.spring2ktor-server")
}
spring2ktorServer {
generatedPackage.set("com.example.generated.springktor")
}
这个插件会自动:
- 应用
com.google.devtools.ksp - 注入
spring2ktor-server-processor - 注入
spring2ktor-server-core - 添加
compileOnly("org.springframework:spring-web")
如果你需要最低层手动控制,仍然可以继续使用原始依赖写法:
plugins {
kotlin("jvm")
id("com.google.devtools.ksp")
}
dependencies {
implementation("site.addzero:spring2ktor-server-core:2026.03.13")
ksp("site.addzero:spring2ktor-server-processor:2026.03.13")
compileOnly("org.springframework:spring-web:5.3.21")
}
这里的 compileOnly("org.springframework:spring-web:5.3.21") 是给业务源码里的 Spring 注解和 MultipartFile 过编译用的。
业务模块本身不需要启动 Spring Boot;当前版本的 spring2ktor-server-core 仍会为 MultipartFile 兼容携带 spring-web 运行时依赖。
如果你的接口返回 JSON,通常还会需要:
dependencies {
implementation("io.ktor:ktor-server-content-negotiation:3.4.0")
implementation("io.ktor:ktor-serialization-kotlinx-json:3.4.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0")
}
最小示例
package demo
import kotlinx.serialization.Serializable
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestHeader
@Serializable
data class EchoRequest(
val name: String,
)
@Serializable
data class EchoResponse(
val message: String,
)
@GetMapping("/hello/{id}")
fun hello(
@PathVariable id: Int,
name: String,
@RequestHeader("X-Trace") trace: String,
): String {
return "$id:$name:$trace"
}
@PostMapping("/echo")
suspend fun echo(@RequestBody body: EchoRequest): EchoResponse {
return EchoResponse("echo:${body.name}")
}
说明:
- 顶层函数可以直接写
- 顶层函数如果有公共前缀,可以在文件顶部写
@file:RequestMapping("/base") - 不强制所有方法都写
suspend - 未标注参数默认按
@RequestParam处理,所以例子里的name等价于@RequestParam("name") - 成功时直接
return - 失败时直接
throw - 返回
null会写成204 No Content - 返回
Unit且你没有手动写响应时,会写成200 OK
文件级前缀示例:
@file:RequestMapping("/api/demo")
package demo.routes
import org.springframework.web.bind.annotation.GetMapping
import site.addzero.springktor.runtime.RequestMapping
@GetMapping("/health")
fun readHealth(): String {
return "ok"
}
在 Ktor 中启用
生成代码后,在你的 Application.module 里手动注册:
package demo
import demo.generated.springktor.registerGeneratedSpringRoutes
import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.Application
import io.ktor.server.application.install
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.routing.routing
fun Application.module() {
install(ContentNegotiation) {
json()
}
routing {
registerGeneratedSpringRoutes()
}
}
如果你用了 @RestController,控制器实例需要你自己交给 Koin 或其他 DI 容器管理。
以 Koin 为例:
dependencies {
implementation("io.insert-koin:koin-ktor:<your-version>")
}
fun Application.module() {
install(ContentNegotiation) {
json()
}
install(org.koin.ktor.plugin.Koin) {
modules(
org.koin.dsl.module {
single { GreetingService() }
single { UserController(get()) }
}
)
}
routing {
registerGeneratedSpringRoutes()
}
}
spring2ktor-server 不再生成 Application.generatedSpringApplication() 或 generatedSpringKoinModule;
应用装配和依赖注入完全由业务自己控制。
生成出来的 API
处理器只会生成这个总入口:
fun Route.registerGeneratedSpringRoutes()
还会按源文件 / Controller 生成更细粒度的注册函数,例如:
fun Route.registerSmokeHandlersSpringRoutes()fun Route.registerSmokeControllerSpringRoutes()
默认生成包名是:
<你的业务包>.generated.springktor
例如源码在 demo 包下,默认就会生成到:
demo.generated.springktor
也可以手动指定:
ksp {
arg("springKtor.generatedPackage", "com.example.generated.springktor")
}
支持的 Spring 注解
HTTP 路由:
@GetMapping@PostMapping@PutMapping@DeleteMapping@PatchMapping@RequestMapping
参数绑定:
@PathVariable@RequestParam@RequestBody@RequestHeader@RequestPartMultipartFileList<MultipartFile>
类式兼容:
@RestController- 类级别
@RequestMapping
参数规则
参数解析优先级如下:
- Ktor 上下文类型直接注入
- 显式 Spring 参数注解按注解绑定
MultipartFile/List<MultipartFile>按 multipart part 名绑定- 未标注参数默认按
@RequestParam
目前支持直接注入的 Ktor 类型包括:
ApplicationCallApplicationRoutingContextApplicationRequestApplicationResponse
这意味着你可以在 Spring 风格 handler 里直接写 Ktor 原生响应逻辑。
例如文件下载:
import io.ktor.http.HttpHeaders
import io.ktor.http.ContentType
import io.ktor.server.application.ApplicationCall
import io.ktor.server.response.header
import io.ktor.server.response.respondBytes
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
@GetMapping("/download/{name}")
suspend fun download(
@PathVariable name: String,
call: ApplicationCall,
) {
call.response.header(HttpHeaders.ContentDisposition, """attachment; filename="$name.txt"""")
call.respondBytes("hello:$name".encodeToByteArray(), ContentType.Text.Plain)
}
例如 SSE:
import io.ktor.http.ContentType
import io.ktor.server.application.ApplicationCall
import io.ktor.server.response.respondTextWriter
import org.springframework.web.bind.annotation.GetMapping
@GetMapping("/sse/messages")
suspend fun sseMessages(call: ApplicationCall) {
call.respondTextWriter(contentType = ContentType.Text.EventStream) {
write("data:hello\n\n")
flush()
write("data:world\n\n")
}
}
@RestController 兼容示例
package demo
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
class GreetingService {
fun greet(id: Int): String = "hello-$id"
}
@RestController
@RequestMapping("/users")
class UserController(
private val greetingService: GreetingService,
) {
@GetMapping("/{id}")
fun getUser(@PathVariable id: Int): String {
return greetingService.greet(id)
}
}
这类写法至少需要:
compileOnly("org.springframework:spring-web:5.3.21")- 你自己的 DI 注册,例如 Koin
single { GreetingService() }和single { UserController(get()) }
目前不做的东西
当前 MVP 主要面向 HTTP JSON API。
不在当前支持范围内的能力包括:
- Spring MVC 视图渲染
ResponseEntity- WebSocket 注解式生成
- 流式文件下载专用返回类型抽象
- 更完整的 SSE 抽象
其中 WebSocket 目前建议直接写原生 Ktor 路由,与生成路由并存:
import io.ktor.server.routing.Route
import io.ktor.server.websocket.webSocket
import io.ktor.websocket.Frame
import io.ktor.websocket.readText
import io.ktor.websocket.send
fun Route.registerWsRoutes() {
webSocket("/ws/echo") {
for (frame in incoming) {
if (frame is Frame.Text) {
send(Frame.Text("echo:${frame.readText()}"))
}
}
}
}
一句话总结
如果你想要的是“写起来像 Spring Controller,但运行起来是 Ktor”,这个处理器就是干这个的。
Maven / Gradle
已发布至 Maven Central。
implementation("site.addzero:spring2ktor-server-processor:latest version")
<dependency>
<groupId>site.addzero</groupId>
<artifactId>spring2ktor-server-processor</artifactId>
<version>latest version</version>
</dependency>