自定义Quarkus CDI TenantScoped上下文在应用中未生效问题排查
自定义TenantScoped上下文失效问题排查与解决
核心问题分析
你遇到的问题核心原因是Quarkus对final类型(如AtomicLong)无法生成CDI代理,导致自定义TenantScoped上下文无法拦截实例的创建与获取,最终被降级为ApplicationScoped处理。Quarkus的CDI实现依赖代理类来实现自定义Scope的上下文切换,而final类无法被继承生成代理,因此会跳过自定义上下文逻辑。
具体解决步骤
1. 替换final类型为可代理的包装类
将AtomicLong包装在一个非final的自定义类中,让Quarkus能够生成代理类:
public class TenantAtomicLong { private final AtomicLong value = new AtomicLong(-1); public long get() { return value.get(); } public void set(long newValue) { value.set(newValue); } }
然后修改Produces方法和注入点:
@ApplicationScoped public class Service { @Inject @Named("tenantLong") TenantAtomicLong tenantScopedLong; public void setLong(long number) { tenantScopedLong.set(number); } public long getLong() { return tenantScopedLong.get(); } @TenantScoped @Named("tenantLong") @Produces TenantAtomicLong tenantLong() { return new TenantAtomicLong(); } }
2. 确保上下文激活逻辑正确
自定义上下文需要在请求生命周期内被激活/销毁,检查你的TenantScopeContext实现是否包含以下逻辑:
- 实现
ActivatableContext接口,确保上下文能被正确激活 - 通过Quarkus扩展绑定生命周期事件,比如在请求开始时调用
activate(),结束时调用deactivate()
示例激活逻辑(扩展构建步骤):
@BuildStep void registerContextActivator(BuildProducer<ServletFilterBuildItem> filterProducer) { filterProducer.produce(ServletFilterBuildItem.builder("TenantContextFilter", TenantContextFilter.class) .order(100) .build()); }
对应的Filter实现:
public class TenantContextFilter implements Filter { @Inject TenantScopeContext tenantContext; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { tenantContext.activate(); // 根据实际需求传递上下文参数 chain.doFilter(request, response); } finally { tenantContext.deactivate(); } } }
3. 验证Scope注册的正确性
检查你的构建步骤是否在正确的阶段注册上下文:
ContextRegistrationPhaseBuildItem是正确的注册阶段,但确保没有其他扩展覆盖了你的Scope配置- 确认
CustomScopeBuildItem正确声明了TenantScoped.class,且没有重复注册的情况
额外注意点
- 避免对final类、基本类型或JDK核心不可变类型使用自定义Scope,这类类型无法生成CDI代理
- 单元测试中可能因为环境简化,代理生成逻辑被绕过,导致上下文正常工作,但实际应用中严格遵循代理规则
内容的提问来源于stack exchange,提问作者Herr Derb
相关产品推荐
相关产品推荐

