You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

是否应允许将Kotlin挂起函数用作合法的CDI生产者/Bean?

Kotlin挂起函数与Jakarta CDI的适配问题

根据Jakarta CDI 2.0(及4.0)规范:

若生产者方法的返回类型包含通配符类型参数,或是组件类型包含通配符类型参数的数组类型,容器会自动检测到该问题并将其视为定义错误。

CDI规范的定义不够精确,不清楚它指的是类定义还是JVM生成的字节码,这会直接影响Kotlin挂起函数的适配逻辑。

Kotlin编译器会将挂起函数转换为延续传递风格(CPS):分析协程的执行上下文,将其转换为带有表示协程状态的局部变量的状态机。由于Kotlin不支持联合类型,转换后会自动添加Continuation和通配符结果(Any?)。

当多个挂起函数处于同一状态机中,与函数调用关联的同一个Continuation对象可能会在它们之间传递,具体取决于多种执行因素。我的理解是,Continuation会在首次调用挂起函数的运行时实例化,不同上下文调用同一函数时会创建不同的Continuation实例。

我基于Kotlin/Arrow、Spring、Quarkus、Micronaut搭建了多个探索项目,将六边形端口定义为类型别名:

typealias AssignKeyboard = suspend (String) -> Either<KeyboardError, Unit>
typealias AssignMouse = suspend (String) -> Either<MouseError, Unit>

生产者Bean的定义示例:

@ApplicationScoped // 或@Bean、@Singleton
// @Named(BeanName.ASSIGN_KEYBOARD)
fun assignLocalKeyboardBean(shellAdapter: ShellAdapter): AssignKeyboard = // 业务实现代码

@ApplicationScoped // 或@Bean、@Singleton
// @Named(BeanName.ASSIGN_MOUSE)
fun assignLocalMouseBean(shellAdapter: ShellAdapter): AssignMouse = // 业务实现代码

注入点示例:

class KeyboardController(
// @Inject
// @Named(BeanName.ASSIGN_KEYBOARD)
   private val assignKeyboard: AssignKeyboard,
) {
// 业务实现代码
}

各框架对挂起函数生产者的支持表现存在差异:

  • Spring支持将挂起函数用作生产者,无需指定限定符即可正常注入
  • Quarkus曾支持通过挂起函数创建生产者(近期已禁用该特性),必须借助限定符才能完成注入,它对该类型的表示示例为:
    kotlin.jvm.functions.Function2<String, ? super kotlin.coroutines.Continuation<? super arrow.core.Either<? extends rg.KeyboardError, ? extends kotlin.Unit>>, ?>
    
  • Micronaut在未使用@Named限定符时,会检测到通配符问题并判定为定义错误

核心疑问:
CDI应当如何适配Kotlin挂起函数?挂起函数能否安全应用于CDI场景?是否需要引入智能分析器,在类型校验阶段忽略与注入上下文强绑定的Continuation及其结果?

更多关于Kotlin协程的细节可参考Kotlin官方规范中的异步编程章节。

更新
完整源代码可查看以下仓库:

  • cdi-quarkus
  • cdi-spring
  • cdi-micronaut

内容的提问来源于stack exchange,提问作者Robert Gonciarz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 09:35:23