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

Micronaut @RequestScope每次注入生成新实例,如何正确注入Singleton?

问题原因

  1. 生命周期不匹配:ExampleService是单例(@Singleton),启动阶段就完成初始化。直接注入RequestContext时,这个实例是启动时创建的,不属于任何请求;而ExampleController中注入的RequestContext是请求到来时生成的,二者是完全独立的实例,因此Service无法获取Controller设置的值。
  2. Kotlin类默认final导致代理失效:Micronaut实现@RequestScope依赖动态代理,但Kotlin类默认是final的,无法生成代理。这使得Singleton注入的RequestContext不会被代理包装,无法在请求时动态获取当前上下文的实例。

解决方法

方法一:使用Provider延迟获取请求实例

这是最稳妥的方案,不受类是否final的限制:

  1. 修改ExampleService,注入Provider<RequestContext>,并在方法内部调用get()获取当前请求的实例:
@Singleton
class ExampleService(
    private val requestContextProvider: Provider<RequestContext>
) {
    fun example() {
        val currentContext = requestContextProvider.get()
        println(currentContext.foo) // 输出"foo"
    }
}
  1. RequestContext保持原定义即可,无需修改。

方法二:让RequestContext支持代理(改为open类)

如果希望直接注入RequestContext而非Provider,需要将类改为open,允许Micronaut生成代理:

  1. 修改RequestContext为open类:
@RequestScope
open class RequestContext {
    var foo: String? = null
}
  1. ExampleService可直接注入RequestContext,Micronaut会自动生成代理,每次调用实例方法时都会转发到当前请求的实际实例:
@Singleton
class ExampleService(
    private val requestContext: RequestContext
) {
    fun example() {
        println(requestContext.foo) // 输出"foo"
    }
}

为什么之前用Provider没效果?

如果之前尝试Provider但未生效,大概率是在ExampleService的构造函数中直接调用provider.get()并保存了实例,而非在每次请求的方法内部调用。错误示例:

// 错误写法:构造函数中获取的是启动时的非请求实例
@Singleton
class ExampleService(
    requestContextProvider: Provider<RequestContext>
) {
    private val requestContext = requestContextProvider.get()
    
    fun example() {
        println(requestContext.foo) // 依然为null
    }
}

必须在请求处理的方法内部调用get(),才能获取当前请求对应的实例。

内容的提问来源于stack exchange,提问作者Tomasz Świstak

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 01:20:12