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
相关产品推荐
相关产品推荐

