Quarkus:如何在非CDI Bean中注入并使用CDI Bean?
问题根源
Quarkus的CDI依赖注入仅对容器托管的Bean生效(即添加了@ApplicationScoped、@Singleton、@RequestScoped等CDI注解的类)。你的Test类是普通Java类,不在容器管理范围内,所以@Inject注解不会被处理,注入的字段自然为null。
同时你的现有代码存在实例冲突问题:
Configuration类同时使用Quarkus的@Singleton注解和手动实现的静态单例模式,会生成两个完全独立的实例(容器管理的实例、getInstance()手动创建的实例)ConfigurationProducer的@Produces方法返回的是手动创建的实例,脱离了容器管理范畴
解决方案
方案一:将Test类转为CDI Bean
如果Test类可以由容器管理,给它添加CDI作用域注解(比如@ApplicationScoped),让容器接管它的生命周期,@Inject即可正常生效:
@ApplicationScoped public class Test { @Inject @Named("singletonConfiguration") Configuration configuration; public void doIt() { System.out.println("TEST"); System.out.println(configuration); // 此时会注入容器管理的实例 } }
同时需要移除Configuration类的手动单例代码,避免实例冲突:
@Named("singletonConfiguration") @Singleton public class Configuration { // 移除静态INSTANCE和getInstance方法,由Quarkus全权管理单例 public Configuration() { // 初始化逻辑写在这里 } }
方案二:在非Bean类中手动获取Bean实例
如果Test类必须是普通类(无法转为容器托管Bean),可以使用Quarkus提供的CDI.current()工具类,手动从容器中获取Bean实例:
import jakarta.enterprise.inject.Instance; import jakarta.enterprise.inject.Named; import jakarta.enterprise.inject.spi.CDI; import jakarta.enterprise.util.AnnotationLiteral; public class Test { public void doIt() { // 通过CDI API获取容器内的Configuration实例 Configuration configuration = CDI.current() .select(Configuration.class, new NamedLiteral("singletonConfiguration")) .get(); System.out.println("TEST"); System.out.println(configuration); } // 辅助类:用于匹配@Named注解 private static class NamedLiteral extends AnnotationLiteral<Named> implements Named { private final String value; public NamedLiteral(String value) { this.value = value; } @Override public String value() { return value; } } }
同样需要移除Configuration类的手动单例代码,确保使用的是容器管理的实例。
方案三:修正Producer实现(可选)
如果想保留ConfigurationProducer,需要让它返回容器管理的实例,而非手动创建的:
@Named("producerConfiguration") @Singleton public class ConfigurationProducer { @Inject @Named("singletonConfiguration") Configuration config; @Produces @ApplicationScoped public Configuration produceConfiguration() { return config; // 返回容器托管的实例 } }
此时在转为Bean的Test类中,可直接注入Producer生成的实例:
@ApplicationScoped public class Test { @Inject @Named("producerConfiguration") Configuration configuration; public void doIt() { System.out.println(configuration); } }
注意事项
- 禁止同时使用CDI的单例注解和手动单例模式,这会导致实例不一致,引发难以排查的问题。
- Quarkus中
@Singleton(JEE标准)和@ApplicationScoped(CDI作用域)的区别:@Singleton是严格单例,无代理;@ApplicationScoped默认也是单例,但支持代理特性,更适合大多数业务场景。
内容的提问来源于stack exchange,提问作者Dmitry Sinyavskiy
相关产品推荐
相关产品推荐

