Micronaut @RequestScope每次注入生成新实例,如何正确注入Singleton?
问题原因
- 生命周期不匹配:
ExampleService是单例(@Singleton),启动阶段就完成初始化。直接注入RequestContext时,这个实例是启动时创建的,不属于任何请求;而ExampleController中注入的RequestContext是请求到来时生成的,二者是完全独立的实例,因此Service无法获取Controller设置的值。 - Kotlin类默认final导致代理失效:Micronaut实现
@RequestScope依赖动态代理,但Kotlin类默认是final的,无法生成代理。这使得Singleton注入的RequestContext不会被代理包装,无法在请求时动态获取当前上下文的实例。
解决方法
方法一:使用Provider延迟获取请求实例
这是最稳妥的方案,不受类是否final的限制:
- 修改
ExampleService,注入Provider<RequestContext>,并在方法内部调用get()获取当前请求的实例:
@Singleton class ExampleService( private val requestContextProvider: Provider<RequestContext> ) { fun example() { val currentContext = requestContextProvider.get() println(currentContext.foo) // 输出"foo" } }
RequestContext保持原定义即可,无需修改。
方法二:让RequestContext支持代理(改为open类)
如果希望直接注入RequestContext而非Provider,需要将类改为open,允许Micronaut生成代理:
- 修改
RequestContext为open类:
@RequestScope open class RequestContext { var foo: String? = null }
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
相关产品推荐
相关产品推荐

