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

CDI Bean可通过查找获取但无法注入的问题求助

搞定CDI扩展里接口代理Bean没法通过限定符注入的问题

嘿,我瞅出来你这问题的核心了:你用CDI扩展注册的接口代理Bean,能用CDI.current()直接拿,但用@Inject @MyBean注入的时候就一直是null。这大概率是因为你的Bean定义没加对限定符,或者没在扩展里把Bean实例正确注册到CDI容器里。

接下来咱们一步步把这个问题解决掉:

1. 先把限定符和标记注解的定义补全

首先得确保@MyBean是个合法的CDI限定符,@RegisterAsMyBean是用来标记需要生成代理的接口的注解:

// 限定符@MyBean的定义
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface MyBean {
    // 可选:如果需要区分不同实例,可以加属性
}

// 标记接口的@RegisterAsMyBean注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface RegisterAsMyBean {
}

2. 修正Extension:把Bean实例注册到CDI容器里

你之前的Extension只收集了要代理的接口,但没把对应的Bean注册进容器。得加一个BeforeBeanDiscovery的观察者,把每个接口对应的代理Bean注册进去:

public class MyExtension implements Extension {
    private final List<Class<?>> proxyInterfaces = new ArrayList<>();

    // 收集带@RegisterAsMyBean的接口,同时否决原类型避免CDI瞎尝试
    public void observeAnnotatedTypes(@Observes @WithAnnotations(RegisterAsMyBean.class) ProcessAnnotatedType<?> pat) {
        Class<?> javaClass = pat.getAnnotatedType().getJavaClass();
        if (javaClass.isInterface()) {
            proxyInterfaces.add(javaClass);
            pat.veto();
        }
    }

    // 在Bean发现阶段,为每个接口注册对应的代理Bean
    public void registerProxyBeans(@Observes BeforeBeanDiscovery bbd, BeanManager beanManager) {
        for (Class<?> intf : proxyInterfaces) {
            Bean<Object> proxyBean = new BeanCreator(intf, beanManager);
            bbd.addBean(proxyBean);
        }
    }
}

3. 完善BeanCreator:补全限定符、类型和Scope

你的BeanCreator必须明确告诉CDI:这个Bean支持什么类型、用什么限定符、属于什么Scope。这就是之前注入失败的关键——CDI找不到匹配的Bean:

public class BeanCreator implements Bean<Object> {
    private final Class<?> interfaceType;
    private final Set<Annotation> qualifiers;
    private final Set<Type> types;
    private final Class<? extends Annotation> scope;

    public BeanCreator(Class<?> interfaceType, BeanManager beanManager) {
        this.interfaceType = interfaceType;
        // 绑定@MyBean限定符,这样@Inject @MyBean才能匹配到这个Bean
        this.qualifiers = Collections.singleton(MyBeanLiteral.INSTANCE);
        // 声明Bean的类型:包含目标接口和Object类
        this.types = new HashSet<>();
        this.types.add(interfaceType);
        this.types.add(Object.class);
        // 设置Scope为@Dependent(默认Scope,也可以换成@Singleton之类的)
        this.scope = Dependent.class;
    }

    @Override
    public Object create(CreationalContext<Object> creationalContext) {
        // 这里写你的代理生成逻辑,比如用JDK动态代理或者ByteBuddy
        return Proxy.newProxyInstance(
                interfaceType.getClassLoader(),
                new Class[]{interfaceType},
                (proxy, method, args) -> {
                    if ("myMethod".equals(method.getName())) {
                        System.out.println("代理执行MyInterface.myMethod()啦!");
                        return null;
                    }
                    return method.invoke(proxy, args);
                }
        );
    }

    @Override
    public void destroy(Object instance, CreationalContext<Object> creationalContext) {
        // 按需清理资源
        creationalContext.release();
    }

    // 下面是Bean接口必须实现的方法,直接返回咱们预先定义好的属性就行
    @Override
    public Set<Type> getTypes() { return types; }
    @Override
    public Set<Annotation> getQualifiers() { return qualifiers; }
    @Override
    public Class<? extends Annotation> getScope() { return scope; }
    @Override
    public String getName() { return interfaceType.getName() + "_proxy"; }
    @Override
    public Set<Class<? extends Annotation>> getStereotypes() { return Collections.emptySet(); }
    @Override
    public boolean isAlternative() { return false; }
    @Override
    public boolean isNullable() { return false; }
    @Override
    public Set<InjectionPoint> getInjectionPoints() { return Collections.emptySet(); }

    // 为@MyBean创建一个Literal实现,方便代码里直接用
    private static class MyBeanLiteral extends AnnotationLiteral<MyBean> implements MyBean {
        public static final MyBeanLiteral INSTANCE = new MyBeanLiteral();
    }
}

4. 验证注入是否正常

现在你可以按需求定义接口:

@RegisterAsMyBean
public interface MyInterface {
    void myMethod();
}

然后在任何CDI管理的Bean里正常注入:

@Inject
@MyBean
private MyInterface myInterface;

public void testProxy() {
    myInterface.myMethod(); // 应该能正常执行代理里的逻辑
}

为啥之前会失败?

总结一下你之前的实现缺失的关键点:

  • 限定符没绑定:Bean没声明@MyBean限定符,CDI找不到和@Inject @MyBean匹配的Bean
  • Bean没注册进容器:Extension只收集了接口,但没把代理Bean注册到CDI容器中
  • 类型没声明:Bean没明确告诉CDI它可以作为MyInterface类型被注入

内容的提问来源于stack exchange,提问作者Miha Jamsek

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:28:36