跳转到主要内容
版本:v2.2.0-0.13.1

自定义转换器

本指南介绍当默认转换器不能满足您的特定需求时,如何创建自定义转换器。

概述

如果默认转换器不能满足您的需求,您可以创建完全自定义的转换器。 例如,如果您想在不使用 kotlinx.coroutines.runBlocking 的情况下实现阻塞逻辑,或者如果您需要平台特定的行为。

信息

JVM 阻塞/异步转换器的完全自定义实现可以在以下位置找到: simbot's BlockingRunner

已知问题

自定义转换器函数不能放在使用编译器插件的同一模块中。它们需要 在单独的模块中创建。

更多信息:#100

基本设置

使用自定义转换器时,您通常不需要默认的注解和运行时依赖项:

suspendTransformPlugin {
// 如果自定义,那么您可能不会使用我们提供的注解和运行时。
includeAnnotation = false
includeRuntime = false

transformers {
// 您的自定义转换器配置
}
}

创建自定义转换器

让我们逐步创建一个自定义转换器。我们将创建一个使用自定义 inBlock 函数的 @JBlock 注解。

步骤 1:定义您的自定义组件

首先,定义您的自定义注解和转换函数:

// 您的自定义注解
annotation class JBlock(
val baseName: String = "",
val suffix: String = "Blocking",
val asProperty: Boolean = false
)

// 您的自定义转换函数
fun <T> inBlock(block: suspend () -> T): T {
// 您的自定义实现
TODO("Your impl")
}
约定

根据约定:转换函数的第一个参数 应该是类型为 suspend () -> T 的 lambda。

步骤 2:配置注解

在您的转换器中配置注解属性:

suspendTransformPlugin {
includeAnnotation = false
includeRuntime = false
transformers {
addJvm {
markAnnotation {
// 您的注解类信息
classInfo {
packageName = "com.example"
className = "JBlock"
}

// 您的注解中的属性名称
baseNameProperty = "baseName" // 默认是 `baseName`
suffixProperty = "suffix" // 默认是 `suffix`
asPropertyProperty = "asProperty" // 默认是 `asProperty`

// 默认值(编译器插件无法获取注解默认值)
defaultSuffix = "Blocking"
defaultAsProperty = false
}
}
}
}

步骤 3:配置转换函数

配置您的自定义转换函数:

suspendTransformPlugin {
transformers {
addJvm {
markAnnotation {
// ... 注解配置
}

// 转换函数信息
transformFunctionInfo {
packageName = "com.example"
functionName = "inBlock"
}

// 返回类型配置
transformReturnType = null // null 表示与原函数相同的类型
transformReturnTypeGeneric = false // 如果返回类型有泛型则为 true
}
}
}

高级配置

自定义属性名称

您可以在注解中使用不同的属性名称:

annotation class JBlock(
val myBaseName: String = "",
val mySuffix: String = "Blocking",
val myAsProperty: Boolean = false
)

配置映射:

suspendTransformPlugin {
transformers {
addJvm {
markAnnotation {
classInfo {
packageName = "com.example"
className = "JBlock"
}

// 映射到您的自定义属性名称
baseNameProperty = "myBaseName"
suffixProperty = "mySuffix"
asPropertyProperty = "myAsProperty"

defaultSuffix = "Blocking"
defaultAsProperty = false
}
}
}
}

返回类型配置

相同返回类型

对于返回与原函数相同类型的函数:

transformReturnType = null
transformReturnTypeGeneric = false

泛型返回类型

对于返回包含原类型的泛型类型的函数(例如,CompletableFuture<T>):

transformReturnType = "java.util.concurrent.CompletableFuture"
transformReturnTypeGeneric = true

特定返回类型

对于返回没有泛型的特定类型的函数(例如,Job):

transformReturnType = "kotlinx.coroutines.Job"
transformReturnTypeGeneric = false

注解管理

向原函数添加注解

向原挂起函数添加注解:

suspendTransformPlugin {
transformers {
addJvm {
// ... 其他配置

// 向原函数添加 @JvmSynthetic
addOriginFunctionIncludeAnnotation {
classInfo {
packageName = "kotlin.jvm"
className = "JvmSynthetic"
}
repeatable = false // 默认是 false
}
}
}
}

向生成的函数添加注解

向生成的合成函数添加注解:

suspendTransformPlugin {
transformers {
addJvm {
// ... 其他配置

// 向生成的函数添加自定义 @JApi 注解
addSyntheticFunctionIncludeAnnotation {
classInfo {
packageName = "com.example"
className = "JApi"
}
includeProperty = true // 可以添加到属性
}
}
}
}

复制注解

启用从原函数到合成函数的注解复制:

suspendTransformPlugin {
transformers {
addJvm {
// ... 其他配置

// 启用注解复制
copyAnnotationsToSyntheticFunction = true
copyAnnotationsToSyntheticProperty = true // 对于属性

// 从复制中排除特定注解
addCopyAnnotationExclude {
classInfo {
packageName = "kotlin.jvm"
className = "JvmSynthetic"
}
}
}
}
}

完整示例

这是一个自定义转换器的完整示例:

自定义组件

// 自定义注解
annotation class JBlock(
val myBaseName: String = "",
val mySuffix: String = "Blocking",
val myAsProperty: Boolean = false
)

// 自定义警告注解
@RequiresOptIn(message = "Api for Java", level = RequiresOptIn.Level.WARNING)
@Retention(AnnotationRetention.BINARY)
annotation class JApi

// 自定义转换函数
fun <T> inBlock(block: suspend () -> T): T {
// 您的自定义阻塞实现
TODO("Your impl")
}

配置

suspendTransformPlugin {
includeAnnotation = false
includeRuntime = false
transformers {
addJvm {
// 注解配置
markAnnotation {
classInfo {
packageName = "com.example"
className = "JBlock"
}

baseNameProperty = "myBaseName"
suffixProperty = "mySuffix"
asPropertyProperty = "myAsProperty"

defaultSuffix = "Blocking"
defaultAsProperty = false
}

// 转换函数配置
transformFunctionInfo {
packageName = "com.example"
functionName = "inBlock"
}

// 注解管理
copyAnnotationsToSyntheticFunction = true
copyAnnotationsToSyntheticProperty = true

// 向原函数添加 @JvmSynthetic
addOriginFunctionIncludeAnnotation {
classInfo {
from(SuspendTransformConfigurations.jvmSyntheticClassInfo)
}
repeatable = false
}

// 向生成的函数添加 @JApi
addSyntheticFunctionIncludeAnnotation {
classInfo {
packageName = "com.example"
className = "JApi"
}
includeProperty = true
}

// 从复制中排除 @JvmSynthetic
addCopyAnnotationExclude {
from(SuspendTransformConfigurations.jvmSyntheticClassInfo)
}
}
}
}

多用途注解

您可以通过使用不同的属性名称创建与多个转换器一起工作的注解:

注解定义

annotation class JTrans(
val blockingBaseName: String = "",
val blockingSuffix: String = "Blocking",
val blockingAsProperty: Boolean = false,

val asyncBaseName: String = "",
val asyncSuffix: String = "Async",
val asyncAsProperty: Boolean = false
)

配置

suspendTransformPlugin {
includeAnnotation = false
includeRuntime = false
transformers {
// 阻塞转换器
addJvm {
markAnnotation {
classInfo {
packageName = "com.example"
className = "JTrans"
}
baseNameProperty = "blockingBaseName"
suffixProperty = "blockingSuffix"
asPropertyProperty = "blockingAsProperty"
defaultSuffix = "Blocking"
defaultAsProperty = false
}

transformFunctionInfo {
packageName = "com.example"
functionName = "inBlock"
}
// ... 其他配置
}

// 异步转换器
addJvm {
markAnnotation {
classInfo {
packageName = "com.example"
className = "JTrans"
}
baseNameProperty = "asyncBaseName"
suffixProperty = "asyncSuffix"
asPropertyProperty = "asyncAsProperty"
defaultSuffix = "Async"
defaultAsProperty = false
}

transformFunctionInfo {
packageName = "com.example"
functionName = "inAsync"
}
// ... 其他配置
}
}
}

内置配置

插件在 love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations 中提供了一些常见配置:

// 常见注解类信息
SuspendTransformConfigurations.jvmSyntheticClassInfo
SuspendTransformConfigurations.kotlinJsExportIgnoreClassInfo

// 在您的配置中使用它们
addCopyAnnotationExclude {
from(SuspendTransformConfigurations.jvmSyntheticClassInfo)
}

其他

MarkName

版本 0.13.0

有关自定义注解中 markName 的配置, 请参考 MarkName