Quarkus同一Bean内不同注入点的自定义InjectableContext无法区分问题
问题
我正在实现一个用于有状态Bean的@TenantScope自定义作用域(不使用嵌套映射),实现了io.quarkus.arc.InjectableContext接口。当前自定义上下文在单个注入点场景下工作正常,能根据tenantId获取对应实例,但同一Bean内存在两个同类型@TenantScoped注入点时,会出现同一实例被重复注入的问题。排查后发现Contextual无法区分这两个注入点,请问如何修改InjectableContext的实现,让每个注入点获取不同实例?
现有get方法实现
@Override public <T> T get(Contextual<T> contextual, CreationalContext<T> creationalContext) { String tenantId = getTenantIdFromContex(); Map<String, ContextInstanceHandle<?>> tenantMap = contextualMap.computeIfAbsent(contextual, c -> new HashMap<>()); @SuppressWarnings("unchecked") ContextInstanceHandle<T> contextInstanceHandle = (ContextInstanceHandle<T>) tenantMap.computeIfAbsent(tenantId, t -> { if (creationalContext == null) { return null; } T createdInstance = contextual.create(creationalContext); return new ContextInstanceHandleImpl<T>( (InjectableBean<T>) contextual, createdInstance, creationalContext); }); return contextInstanceHandle.get(); } @SuppressWarnings("unchecked") @Override public <T> T get(Contextual<T> contextual) { String tenantId = getTenantIdFromContex(); if(!contextualMap.containsKey(contextual) || !contextualMap.get(contextual).containsKey(tenantId)){ return null; } return ((ContextInstanceHandle<T>) contextualMap.get(contextual).get(tenantId)).get(); }
示例作用域Bean
@TenantScoped class MyTenantScopedQueue(){...}
存在问题的注入场景
class MyBean() { @Inject MyTenantScopedQueue queue; @Inject MyTenantScopedQueue queueForOtherStuff; ... }
解决方案
核心问题是当前仅用Contextual(Bean类型)和tenantId作为实例标识,但同一类型的不同注入点需要区分,因此要引入注入点标识符扩展实例存储键。
1. 调整上下文存储结构
把原有的Map<Contextual<?>, Map<String, ContextInstanceHandle<?>>>三层化:
- 第一层:Bean类型(
Contextual) - 第二层:租户ID(
tenantId) - 第三层:注入点(
InjectPoint)
确保同一租户、同一Bean类型下,不同注入点对应不同实例。
2. 修改get方法实现
利用Quarkus的InjectableCreationalContext获取注入点信息,调整后的代码如下:
// 更新存储结构的泛型定义 private final Map<Contextual<?>, Map<String, Map<InjectPoint, ContextInstanceHandle<?>>>> contextualMap = new ConcurrentHashMap<>(); @Override public <T> T get(Contextual<T> contextual, CreationalContext<T> creationalContext) { String tenantId = getTenantIdFromContex(); // 从CreationalContext中提取注入点 InjectPoint injectPoint = null; if (creationalContext instanceof InjectableCreationalContext) { injectPoint = ((InjectableCreationalContext<?>) creationalContext).getInjectPoint(); } // 逐层初始化存储结构 Map<String, Map<InjectPoint, ContextInstanceHandle<?>>> tenantMap = contextualMap.computeIfAbsent(contextual, c -> new ConcurrentHashMap<>()); Map<InjectPoint, ContextInstanceHandle<?>> injectPointMap = tenantMap.computeIfAbsent(tenantId, t -> new ConcurrentHashMap<>()); @SuppressWarnings("unchecked") ContextInstanceHandle<T> contextInstanceHandle = (ContextInstanceHandle<T>) injectPointMap.computeIfAbsent(injectPoint, ip -> { if (creationalContext == null) { return null; } T createdInstance = contextual.create(creationalContext); return new ContextInstanceHandleImpl<>( (InjectableBean<T>) contextual, createdInstance, creationalContext); }); return contextInstanceHandle.get(); } @SuppressWarnings("unchecked") @Override public <T> T get(Contextual<T> contextual) { // 无CreationalContext时无法获取注入点,可根据业务需求返回null或默认实例 return null; }
3. 关键说明
- 注入点获取:Quarkus的
InjectableCreationalContext实现了标准CreationalContext,能直接提取对应注入点,这是区分不同注入点的核心依据。 - 无CreationalContext场景:
get(Contextual<T>)通常用于手动获取Bean(如通过BeanManager),这类场景没有注入点信息,可根据业务需求决定返回逻辑。 - 可选优化:如果需要支持
@Named注解区分注入点,可在复合键中加入@Named的名称,实现“同名称注入点复用实例”的灵活逻辑。
内容的提问来源于stack exchange,提问作者Herr Derb
相关产品推荐
相关产品推荐

