阅读(2732) (0)

Micronaut Kotlin 支持

2023-02-23 13:53:07 更新

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

创建 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 范围内声明。例如:

Example 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 文件,您现在可以使用运行任务(由 Application 插件提供)来运行您的 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"
    }
}

Kotlin, Kapt and IntelliJ

在撰写本文时,IntelliJ 的内置编译器不直接支持 Kapt 和注释处理。在运行测试或应用程序类之前,您必须将 Intellij 配置为运行 Gradle(或 Maven)编译作为构建步骤。

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


然后将类任务添加为应用程序或测试 testClasses 任务执行的任务:


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

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


使用 Gradle 和 Kapt 进行增量注释处理

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

以下示例演示了如何为您在 com.example 和 io.example 包下定义的注释启用和配置增量注释处理:

Enabling Incremental Annotation Processing in Kapt

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

如果您不为您的自定义注释启用处理,它们将被 Micronaut 忽略,这可能会破坏您的应用程序。

Kotlin 和 AOP 建议

Micronaut 提供了一个不使用反射的编译时 AOP API。当您使用任何 Micronaut AOP Advice 时,它​​会在编译时创建一个子类来提供 AOP 行为。这可能是个问题,因为 Kotlin 类默认是最终的。如果应用程序是使用 Micronaut CLI 创建的,则 Kotlin 全开放插件已配置为在使用 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 Advice,则需要在类和方法级别使用open。

全开放插件不处理方法。如果在方法上声明 AOP 注释,则必须手动将其声明为打开。

Kotlin 和保留参数名称

与 Java 一样,使用 Kotlin 时,方法参数的参数名称数据不会在编译时保留。如果您没有显式定义参数名称并依赖于已编译的外部 JAR,这对 Micronaut 来说可能是个问题。

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

configuration in Gradle

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

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

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

configuration in 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>

协程支持

Kotlin 协程允许您使用命令式代码创建异步应用程序。一个 Micronaut 控制器动作可以是一个挂起函数:

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

  2. 该函数被标记为挂起。

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

  4. 当我们只想返回一个状态时,suspend 函数也可以工作。

您还可以将 Flow 类型用于流式传输服务器和客户端。流式控制器可以返回 Flow,例如:

  1. 定义了生成 application/x-json-stream 的方法 streamHeadlinesWithFlow

  2. Flow是使用flow创建的

  3. 此流发出 100 条消息

  4. 使用 emit suspend 函数进行发射

  5. 消息之间有一秒的延迟

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

  1. @Get 方法定义为处理 APPLICATION_JSON_STREAM 类型的响应

  2. 返回类型是 Flow

协程跟踪上下文传播

Micronaut支持跟踪上下文的传播。如果你从控制器动作一直到所有的服务都使用挂起函数,你不需要做任何特别的事情。然而,当你在一个普通的函数中创建了coroutines时,跟踪传播不会自动发生。你必须使用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()
    }
}

反应性上下文传播

Micronaut 支持从 Reactor 上下文到协程上下文的上下文传播。要启用此传播,您需要包含以下依赖项:

 Gradle Maven 
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
<dependency>
    <groupId>org.jetbrains.kotlinx</groupId>
    <artifactId>kotlinx-coroutines-reactor</artifactId>
</dependency>

有关如何使用该库的更多详细信息,您可以在官方文档中找到。

以下示例显示了如何将 Reactor 上下文从 HTTP 过滤器传播到控制器的协程:

通过从协程上下文中检索 ReactorContext 来访问 Reactor 上下文:

可以使用协程 Reactor 集成来使用挂起函数创建过滤器: