如何在Weld CDI框架中为无控制权类创建自定义Bean并实现指定字段的依赖注入
解决方案:为无控制类创建自定义CDI Bean并实现依赖注入
我明白你的问题——你要给一个没有无参构造、无法修改的类创建合法的CDI Bean,同时还想让CDI自动注入它的特定字段,当前代码因为AnnotatedType没有无参构造导致InjectionTarget无法正常工作,而且你不想依赖Weld的内部实现。下面是基于CDI标准API的完整解决方案,完全符合规范:
第一步:创建自定义InjectionTarget包装器
我们需要包装CDI原生的InjectionTarget,跳过它的实例创建逻辑(因为我们要自己手动创建实例),只保留依赖注入和生命周期回调的能力:
import jakarta.enterprise.context.spi.CreationalContext; import jakarta.enterprise.inject.spi.InjectionTarget; import java.util.Set; import java.util.function.Supplier; public class CustomInjectionTarget<T> implements InjectionTarget<T> { private final InjectionTarget<T> delegate; private final Supplier<T> instanceSupplier; public CustomInjectionTarget(InjectionTarget<T> delegate, Supplier<T> instanceSupplier) { this.delegate = delegate; this.instanceSupplier = instanceSupplier; } @Override public T produce(CreationalContext<T> ctx) { // 直接返回我们预先创建好的实例,绕过CDI的实例创建逻辑 return instanceSupplier.get(); } @Override public void inject(T instance, CreationalContext<T> ctx) { // 委托给原始InjectionTarget执行依赖注入 delegate.inject(instance, ctx); } @Override public void postConstruct(T instance) { // 触发postConstruct生命周期回调 delegate.postConstruct(instance); } @Override public void preDestroy(T instance) { delegate.preDestroy(instance); } @Override public void dispose(T instance) { delegate.dispose(instance); } @Override public Set<InjectionPoint> getInjectionPoints() { return delegate.getInjectionPoints(); } }
第二步:修改Extension中的AfterBeanDiscovery逻辑
调整你的afterBeanDiscovery方法,使用包装后的InjectionTarget来处理注入和生命周期,同时避免手动查找InjectionPoint的繁琐操作:
import jakarta.enterprise.event.Observes; import jakarta.enterprise.inject.spi.AfterBeanDiscovery; import jakarta.enterprise.inject.spi.AnnotatedType; import jakarta.enterprise.inject.spi.BeanAttributes; import jakarta.enterprise.inject.spi.BeanManager; import jakarta.enterprise.inject.spi.InjectionTarget; import jakarta.enterprise.context.spi.CreationalContext; import java.util.function.BiFunction; void afterBeanDiscovery(@Observes AfterBeanDiscovery abd, BeanManager beanManager) { // 创建目标类的AnnotatedType,用于解析元数据 final AnnotatedType<ClassWithoutControl> annotatedType = beanManager.createAnnotatedType(ClassWithoutControl.class); // 获取原始InjectionTarget,它会自动解析所有需要注入的字段/方法 final InjectionTarget<ClassWithoutControl> originalInjectionTarget = beanManager.createInjectionTarget(annotatedType); // 定义自定义Bean的实例创建逻辑 BiFunction<CreationalContext<ClassWithoutControl>, Bean<ClassWithoutControl>, ClassWithoutControl> creator = (creationalContext, bean) -> { // 手动创建目标类实例,使用你自己的构造参数 ClassWithoutControl instance = new ClassWithoutControl(this.paramater1, this.parameter2); // 包装原始InjectionTarget,传入我们的实例 InjectionTarget<ClassWithoutControl> wrappedTarget = new CustomInjectionTarget<>(originalInjectionTarget, () -> instance); // 执行依赖注入:CDI会自动注入ClassWithControl等依赖 wrappedTarget.inject(instance, creationalContext); // 触发postConstruct注解的方法(如果有的话) wrappedTarget.postConstruct(instance); return instance; }; // 构建自定义Bean并添加到CDI容器 customBeanBuilder.setInjectionPoints(originalInjectionTarget.getInjectionPoints()) .setCreator(creator); final BeanAttributes<ClassWithoutControl> beanAttributes = beanManager.createBeanAttributes(annotatedType); customBeanBuilder.setBeanAttributes(beanAttributes); abd.addBean(customBeanBuilder.build()); }
第三步:补充销毁生命周期(可选但推荐)
为了保证CDI Bean的生命周期完整,你需要在自定义Bean的destroy方法中处理preDestroy和销毁逻辑。可以在Extension中维护一个实例与InjectionTarget的映射:
// 在Extension类中添加成员变量 private final WeakHashMap<ClassWithoutControl, InjectionTarget<ClassWithoutControl>> instanceToTargetMap = new WeakHashMap<>(); // 修改creator逻辑,保存实例与InjectionTarget的关联 BiFunction<CreationalContext<ClassWithoutControl>, Bean<ClassWithoutControl>, ClassWithoutControl> creator = (creationalContext, bean) -> { ClassWithoutControl instance = new ClassWithoutControl(this.paramater1, this.parameter2); InjectionTarget<ClassWithoutControl> wrappedTarget = new CustomInjectionTarget<>(originalInjectionTarget, () -> instance); wrappedTarget.inject(instance, creationalContext); wrappedTarget.postConstruct(instance); // 保存映射,用于销毁时使用 instanceToTargetMap.put(instance, wrappedTarget); return instance; }; // 确保你的CustomBean实现destroy方法,比如: public class CustomClassWithoutControlBean implements Bean<ClassWithoutControl> { private final WeakHashMap<ClassWithoutControl, InjectionTarget<ClassWithoutControl>> instanceToTargetMap; // 其他成员变量... public CustomClassWithoutControlBean(WeakHashMap<ClassWithoutControl, InjectionTarget<ClassWithoutControl>> instanceToTargetMap) { this.instanceToTargetMap = instanceToTargetMap; } @Override public void destroy(ClassWithoutControl instance, CreationalContext<ClassWithoutControl> ctx) { InjectionTarget<ClassWithoutControl> target = instanceToTargetMap.remove(instance); if (target != null) { target.preDestroy(instance); target.dispose(instance); } ctx.release(); } // 其他Bean接口方法实现... }
为什么这个方案可行?
- 不依赖Weld内部实现:所有代码都使用CDI规范定义的API(
InjectionTarget、Bean、CreationalContext等),完全符合JSR-365标准,切换其他CDI实现(比如OpenWebBeans)也能正常工作。 - 绕过无参构造限制:通过包装
InjectionTarget,我们跳过了CDI自动创建实例的逻辑,完全由自己控制实例的创建过程,同时保留了CDI的依赖注入和生命周期管理能力。 - 自动解析注入点:使用
originalInjectionTarget.getInjectionPoints()自动获取所有需要注入的字段/方法,避免了手动遍历查找的错误,也支持所有CDI注入注解(@Inject、@Named等)。
内容的提问来源于stack exchange,提问作者Byebye
相关产品推荐
相关产品推荐

