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(); }
- 给各个
HelloWorldDependencyBean添加限定符:
@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
相关产品推荐
相关产品推荐

