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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 14:37:06