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

Quarkus:生成依赖泛型Bean的Bean及泛型丢失问题求解

在Quarkus中生成依赖泛型类型Bean的Bean问题

问题背景

我需要在Quarkus中生成一个依赖另一个泛型类型Bean的Bean,当前使用Arc获取依赖时,运行时泛型信息丢失。现有非泛型依赖的情况可以正常运行,但泛型场景下出现问题。我期望不依赖Arc实现,想了解是否需要使用限定符,以及其他通过Quarkus构建项生成Bean的方式。

现有代码

现有Bean定义

@Singleton
public class DefaultHelloWorld extends HelloWorld<String> {
    public DefaultHelloWorld(final HelloWorldDependency<String> helloWorldDependency) {
        super(helloWorldDependency);
    }

    @Override
    public String sayHello() {
        return "DefaultHelloWorld";
    }
}

生成Bean的构建代码

@BuildStep(onlyIf = ShouldGenerateBean.class)
void helloWorldGenerator(final BuildProducer<GeneratedBeanBuildItem> generatedBeanBuildItemBuildProducer) {
    final ClassOutput beansClassOutput = new GeneratedBeanGizmoAdaptor(generatedBeanBuildItemBuildProducer);
    final ClassCreator beanClassCreator = ClassCreator.builder().classOutput(beansClassOutput)
            .className(HelloWorld.class.getName() + "Generated")
            .signature(
                    SignatureBuilder.forClass()
                            .setSuperClass(
                                    Type.parameterizedType(
                                            Type.classType(HelloWorld.class),
                                            Type.classType(String.class))))
            .build();
    beanClassCreator.addAnnotation(Singleton.class);
    beanClassCreator.addAnnotation(DefaultBean.class);
    // constructor
    final MethodCreator constructor = beanClassCreator.getMethodCreator(MethodDescriptor.INIT, void.class,
            String.format("%s<%s>", HelloWorldDependency.class.getName(), String.class.getName()));
    constructor.setModifiers(Modifier.PUBLIC);
    ResultHandle supportHandle = getFromCDI(constructor, String.format("%s<%s>", HelloWorldDependency.class.getName(), String.class.getName()));
    constructor.invokeSpecialMethod(MethodDescriptor.ofConstructor(HelloWorld.class, HelloWorldDependency.class), constructor.getThis(), supportHandle);
    constructor.returnValue(null);

    // sayHello
    final MethodCreator clazzMethod = beanClassCreator.getMethodCreator("sayHello", Object.class);
    clazzMethod.setModifiers(Opcodes.ACC_PUBLIC);
    clazzMethod.returnValue(clazzMethod.load("HelloWorldGenerated"));
    beanClassCreator.close();
}

// 从Arc获取Bean的工具方法
private ResultHandle getFromCDI(final MethodCreator mc, final String className) {
    // Arc.container().instance(className).get();
    final ResultHandle containerHandle = mc.invokeStaticMethod(MethodDescriptor.ofMethod(Arc.class, "container", ArcContainer.class));
    final ResultHandle instanceHandle = mc.invokeInterfaceMethod(
            MethodDescriptor.ofMethod(ArcContainer.class, "instance", InstanceHandle.class, Class.class,
                    Annotation[].class),
            containerHandle, mc.loadClassFromTCCL(className),
            mc.newArray(Annotation.class, 0));
    return mc.invokeInterfaceMethod(MethodDescriptor.ofMethod(InstanceHandle.class, "get", Object.class), instanceHandle);
}

解决方案

1. 避免运行时手动获取依赖,让CDI自动注入

你当前通过Arc手动获取依赖的方式会丢失泛型信息,因为Arc.container().instance(Class)只能识别原始类型,无法区分泛型参数。正确的做法是让生成的Bean通过构造器声明泛型依赖,由CDI在启动时自动注入,无需依赖Arc。

修改生成构造器的代码,使用Type.parameterizedType定义泛型参数类型,而非字符串格式的类名:

// 定义泛型参数类型
Type helloWorldDependencyStringType = Type.parameterizedType(
        Type.classType(HelloWorldDependency.class),
        Type.classType(String.class));

// 创建构造器,参数为HelloWorldDependency<String>
final MethodCreator constructor = beanClassCreator.getMethodCreator(
        MethodDescriptor.INIT, 
        void.class, 
        helloWorldDependencyStringType);
constructor.setModifiers(Modifier.PUBLIC);

// 直接使用构造器的参数调用父类构造器,无需手动获取依赖
constructor.invokeSpecialMethod(
        MethodDescriptor.ofConstructor(HelloWorld.class, HelloWorldDependency.class), 
        constructor.getThis(), 
        constructor.getMethodParam(0));
constructor.returnValue(null);

这样生成的Bean构造器会直接接收HelloWorldDependency<String>类型的参数,CDI会自动匹配并注入对应的泛型Bean,完全不需要依赖Arc,也不会丢失泛型信息。

2. 多泛型实例场景下使用限定符

如果你的应用中存在多个同原始类型、不同泛型参数的HelloWorldDependency Bean(比如HelloWorldDependency<String>和HelloWorldDependency<Integer>),需要通过自定义限定符来区分它们:

  • 首先定义一个带属性的限定符注解:
@Qualifier
@Target({TYPE, METHOD, PARAMETER, FIELD})
@Retention(RUNTIME)
public @interface ForType {
    Class<?> value();
}
  • 给各个HelloWorldDependency Bean添加限定符:
@Singleton
@ForType(String.class)
public class StringHelloWorldDependency implements HelloWorldDependency<String> {
    // ...
}
  • 生成Bean时,给构造器的参数添加对应的限定符注解:
// 给构造器参数添加@ForType(String.class)注解
AnnotationCreator forTypeAnnotation = constructor.addAnnotation(ForType.class);
forTypeAnnotation.addValue("value", Type.classType(String.class));

这样CDI就能精准匹配到对应泛型参数的Bean。

3. 其他生成Bean的注意事项

  • 确保生成的Bean类签名正确声明泛型父类(你当前的代码已经做到了这一点),避免类型擦除导致的类型转换问题。
  • 使用DefaultBean注解时,要确保没有其他同类型的默认Bean,避免注入冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 16:44:55