Quarkus:拦截器注入请求作用域Bean触发ContextNotActiveException问题排查
我开发了一个Quarkus应用,入口为实现AWS RequestHandler的类。在执行业务逻辑前,希望根据传入参数设置上下文,因此创建了标注@RequestScoped的Bean用于存储上下文,通过setter方法赋值。
尝试使用拦截器实现该逻辑,在拦截器类中注入这个请求作用域Bean,但运行应用时拦截器触发后调试报错:
Method threw 'javax.enterprise.context.ContextNotActiveException'. Cannot evaluate common.service.MyRequestScopedBean_ClientProxy.toString()
另一个以REST接口为入口的Quarkus应用执行相同逻辑则无问题,当前使用Quarkus版本为2.7.5.Final。
相关代码如下:
上下文Bean
@Setter @Getter @RequestScoped public class MyRequestScopedBean { private String context; }
拦截器绑定注解
@InterceptorBinding @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface ContextBinding { }
拦截器类
@Slf4j @Interceptor @ContextBinding public class ContextInterceptor { @Inject MyRequestScopedBean myRequestScopedBean; @AroundInvoke public Object setContext(InvocationContext context) throws Exception { log.info("{}", context); myRequestScopedBean.setContext("Some context"); return context.proceed(); } }
Handler类
@Slf4j @ActivateRequestContext @ContextBinding @Named("Handler") public class Handler implements RequestHandler<Event, Void> { @Override public Void handleRequest(Event event, Context context) { // some logic } }
Quarkus中,@ActivateRequestContext的生效时机是目标方法(即handleRequest)执行时才会激活请求上下文,但拦截器的@AroundInvoke方法会在目标方法执行前触发——此时请求上下文尚未激活,注入的@RequestScoped Bean无法获取到活跃的上下文,因此抛出ContextNotActiveException。
而REST接口场景下,Quarkus会在请求进入时自动激活请求上下文,拦截器执行时上下文已经存在,所以不会出现该错误。
方法1:手动在拦截器中激活请求上下文
通过RequestContextController手动控制请求上下文的激活与关闭,确保操作@RequestScoped Bean时上下文处于活跃状态:
@Slf4j @Interceptor @ContextBinding public class ContextInterceptor { @Inject MyRequestScopedBean myRequestScopedBean; @Inject RequestContextController requestContextController; @AroundInvoke public Object setContext(InvocationContext context) throws Exception { // 手动激活请求上下文 requestContextController.activate(); try { log.info("{}", context); myRequestScopedBean.setContext("Some context"); return context.proceed(); } finally { // 确保请求上下文最终被关闭 requestContextController.deactivate(); } } }
方法2:将@ActivateRequestContext移至拦截器类
移除Handler类上的@ActivateRequestContext,将其标注在拦截器类上,让请求上下文在拦截器执行前就被激活(Quarkus 2.7.5.Final支持此用法):
@Slf4j @Interceptor @ContextBinding @ActivateRequestContext public class ContextInterceptor { // 保持原有拦截器逻辑不变 }
方法3:调整上下文设置逻辑的触发时机
如果业务允许,可以将上下文设置逻辑直接放在handleRequest方法的开头,此时@ActivateRequestContext已经激活了请求上下文,注入的@RequestScoped Bean可以正常使用:
@Slf4j @ActivateRequestContext @Named("Handler") public class Handler implements RequestHandler<Event, Void> { @Inject MyRequestScopedBean myRequestScopedBean; @Override public Void handleRequest(Event event, Context context) { // 直接在方法内设置上下文 myRequestScopedBean.setContext("Some context"); // some logic } }
这种方式可以省去拦截器的使用,适合逻辑简单的场景。
内容的提问来源于stack exchange,提问作者bob

