跳到主要内容

14.3 Kotlin 版 Micronaut

提示

Micronaut 的命令行界面包含对 Kotlin 的特殊支持。要创建 Kotlin 应用程序,请使用 kotlin 语言选项。例如:

创建 Micronaut Kotlin 应用程序

$ mn create-app hello-world --lang kotlin

Micronaut 对 Kotlin 的支持建立在 Kapt 编译器插件的基础上,其中包括对 Java 注解处理器的支持。要在 Micronaut 应用程序中使用 Kotlin,需要添加适当的依赖项,以便在 kt 源文件上配置和运行 Kapt。Kapt 会为你的 Kotlin 类创建 Java "存根 "类,然后由 Micronaut 的 Java 注解处理器进行处理。存根不包含在最终编译的应用程序中。

提示

官方文档中了解有关 kapt 及其功能的更多信息。

使用 Gradle 时,Micronaut 注解处理器在 kapt 作用域中声明。例如

示例 build.gradle

dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" (1)
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
kapt "io.micronaut:micronaut-inject-java" (2)

kaptTest "io.micronaut:micronaut-inject-java" (3)
...
}
  1. 添加 Kotlin 标准库
  2. kapt 作用域下添加 micronaut-inject-java 依赖,以便处理 src/main 中的类
  3. kaptTest 作用域下添加 micronaut-inject-java 依赖,以便处理 src/test 中的类。

有了类似上述的 build.gradle 文件,现在就可以使用运行任务(由应用程序插件提供)运行 Micronaut 应用程序了:

$ ./gradlew run

下面是一个用 Kotlin 编写的控制器示例:

src/main/kotlin/example/HelloController.kt

package example

import io.micronaut.http.annotation.*

@Controller("/")
class HelloController {

@Get("/hello/{name}")
fun hello(name: String): String {
return "Hello $name"
}
}

14.3.1 Kotlin、Kapt 和 IntelliJ

到目前为止,IntelliJ 的内置编译器并不直接支持 Kapt 和注解处理。因此,在运行测试或应用程序类之前,必须配置 Intellij 以运行 Gradle(或 Maven)编译作为构建步骤。

首先,编辑测试或应用程序的运行配置,选择 "运行 Gradle 任务 "作为构建步骤:

kotlin-run-1

然后添加 classes 任务作为应用程序或测试的 testClasses 任务:

kotlin-run-2

现在,当你运行测试或启动应用程序时,Micronaut 会在编译时生成类。

或者,也可以完全将 IntelliJ 的构建/运行操作委托给 Gradle

delegatetogradle

14.3.2 利用 Gradle 和 Kapt 进行增量注解处理

要使用 Kapt 启用 Gradle 增量注解处理,必须向 Kapt 发送使用 Gradle 进行增量注解处理中指定的参数。

下面的示例演示了如何为 com.exampleio.example 包下定义的注解启用和配置增量注解处理:

在 Kapt 中启用增量注解处理

kapt {
arguments {
arg("micronaut.processing.incremental", true)
arg("micronaut.processing.annotations", "com.example.*,io.example.*")
}
}
警告

如果不启用自定义注解处理功能,Micronaut 将忽略这些注解,这可能会导致应用程序崩溃。

14.3.3 Kotlin 和 AOP 通知

Micronaut 提供不使用反射的编译时 AOP API。当你使用任何 Micronaut AOP 通知时,它会在编译时创建一个子类来提供 AOP 行为。这可能会造成问题,因为 Kotlin 类默认为最终类。如果应用程序是使用 Micronaut CLI 创建的,那么 Kotlin all-open 插件会为你配置,当使用 AOP 注解时,它会自动将你的类更改为开放类。要自行配置,可将 Around 类添加到支持的注解列表中。

如果你不想或无法使用全开放插件,则必须将使用 AOP 注解的类声明为开放类:

import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import io.micronaut.http.HttpStatus
import io.micronaut.validation.Validated
import javax.validation.constraints.NotBlank

@Validated
@Controller("/email")
open class EmailController { (1)

@Get("/send")
fun index(@NotBlank recipient: String, (1)
@NotBlank subject: String): HttpStatus {
return HttpStatus.OK
}
}
  1. 如果使用 @Validated AOP 通知,则需要在类和方法级别使用 open
注意

all-open 插件不处理方法。如果在方法上声明 AOP 注解,则必须手动将其声明为 open

14.3.4 Kotlin 和保留参数名称

与 Java 一样,使用 Kotlin 时,方法参数的参数名数据不会在编译时保留。如果不明确定义参数名,而依赖于已编译的外部 JAR,这可能会给 Micronaut 带来问题。

要使用 Kotlin 保留参数名称数据,请在 build.gradle 中设置 javaParameters 选项为 true

在 Gradle 中配置

compileTestKotlin {
kotlinOptions {
jvmTarget = '1.8'
javaParameters = true
}
}
注意

如果使用带有默认方法的接口,请添加 freeCompilerArgs = ["-Xjvm-default=all"] 以便 Micronaut 能识别它们。

如果使用 Maven,则相应配置 Micronaut Maven 插件:

在 Maven 中配置

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- ... -->
<build>
<plugins>
<!-- ... -->
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<configuration>
<javaParameters>true</javaParameters>
<!-- ... -->
</configuration>
<!-- ... -->
</plugin>
<!-- ... -->
</plugins>
</build>
</project>

14.3.5 协程支持

Kotlin 例程允许你使用命令式代码创建异步应用程序。Micronaut 控制器动作可以是 suspend 函数:

  1. 该函数被标记为 suspend,但实际上它不会被挂起。

  2. 函数被标记为 suspend

  3. 调用 delay 以确保函数被挂起,并从不同的线程返回响应。

  4. 当我们只想返回状态时,suspend 函数也能发挥作用。

流式服务器和客户端也可以使用 Flow 类型。例如,流控制器可以返回 Flow

  1. 定义了一个方法 streamHeadlinesWithFlow,该方法产生 application/x-json-stream
  2. 使用 flow 创建一个 Flow
  3. Flow 发出 100 条信息
  4. 使用 emit suspend 函数发送信息
  5. 消息之间有一秒钟的 delay

流客户端可以简单地返回一个 Flow,例如

  1. @Get 方法被定义为处理 APPLICATION_JSON_STREAM 类型的响应
  2. 返回类型是 Flow

14.3.6 协程跟踪上下文传播

Micronaut 支持跟踪上下文传播。如果从控制器动作到所有服务都使用挂起函数,就不需要做任何特殊处理。但是,在常规函数中创建例程时,跟踪传播不会自动发生。你必须使用 HttpCoroutineContextFactory<CoroutineTracingDispatcher> 来创建一个新的 CoroutineTracingDispatcher,并将其用作 CoroutineContext

下面的示例展示了这种情况:

@Controller
class SimpleController(
private val coroutineTracingDispatcherFactory: HttpCoroutineContextFactory<CoroutineTracingDispatcher>
) {
@Get("/runParallelly")
fun runParallelly(): String = runBlocking {
val a = async(Dispatchers.Default + coroutineTracingDispatcherFactory.create()) {
val traceId = MDC.get("traceId")
println("$traceId: Calculating sth...")
calculateSth()
}
val b = async(Dispatchers.Default + coroutineTracingDispatcherFactory.create()) {
val traceId = MDC.get("traceId")
println("$traceId: Calculating sth else...")
calculateSthElse()
}

a.await() + b.await()
}
}

14.3.7 响应式上下文传播

Micronaut 支持从 Reactor 的上下文到 coroutine 上下文的上下文传播。要启用这种传播,你需要加入以下依赖:

implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")

有关如何使用该库的详细信息,参阅官方文档

下面的示例展示了如何从 HTTP 过滤器向控制器的例程传播 Reactor 上下文:

通过检索 coroutine 上下文中的 ReactorContext 访问 Reactor 上下文:

可以使用 coroutines Reactor 集成来使用悬浮函数创建过滤器:

英文链接