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

如何在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接口方法实现...
}

为什么这个方案可行?

  1. 不依赖Weld内部实现:所有代码都使用CDI规范定义的API(InjectionTarget、Bean、CreationalContext等),完全符合JSR-365标准,切换其他CDI实现(比如OpenWebBeans)也能正常工作。
  2. 绕过无参构造限制:通过包装InjectionTarget,我们跳过了CDI自动创建实例的逻辑,完全由自己控制实例的创建过程,同时保留了CDI的依赖注入和生命周期管理能力。
  3. 自动解析注入点:使用originalInjectionTarget.getInjectionPoints()自动获取所有需要注入的字段/方法,避免了手动遍历查找的错误,也支持所有CDI注入注解(@Inject、@Named等)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 12:34:08