Quarkus中CDI Bean注入为null的问题求助
我开发了一个简单的Quarkus后端应用,多数场景下CDI依赖注入运行正常,但存在一处依赖Bean注入后为null的异常情况。待注入的是用于从MongoDB加载数据的UserService类,代码如下:
UserService.java
@Singleton // <= is this annotation correct? Or public class UserService { @Inject // <= this all works fine MongoClient mongoClient; @ConfigProperty(name = "liquido.db-name") String dbName; public Optional<UserModel> getUserById(String userId) { // ... code to fetch user form DB ... } }
另有一个实现org.mongodb.bson.CollectibleCodec的TeamCodec类,用于将MongoDB中的数据解码为POJO。该类中通过@Inject注入的UserService始终为null,调用其getUserById方法时失败,代码如下:
TeamCodec.java
@ApplicationScoped // <= is this annotation correct? public class TeamCodec implements CollectibleCodec<TeamModel> { @Inject UserService userService; // <=== THIS IS ALWAYS NULL??? @Override public TeamModel decode(BsonReader reader, DecoderContext decoderContext) { TeamModel teamModel = new TeamModel(); reader.readStartDocument(); userService.getUserById(...); // <==== THIS FAILS } }
核心原因:MongoDB Codec实例不受CDI容器管理
Quarkus的CDI注入仅对容器创建并管理的Bean实例生效。而MongoDB的CollectibleCodec实例通常由MongoDB驱动自身实例化(比如通过CodecRegistry注册时),并非Quarkus CDI容器创建,因此CDI无法对其执行依赖注入,导致userService始终为null。
具体解决步骤
让CDI容器接管TeamCodec的实例化
不能依赖MongoDB驱动自动创建TeamCodec,需要通过CDI管理它的实例,再手动注册到MongoDB的CodecRegistry中。调整TeamCodec的作用域(推荐)
由于Codec通常是无状态的,使用@Singleton作用域比@ApplicationScoped更合适,可避免不必要的代理开销。手动注册CDI管理的TeamCodec到MongoDB
创建启动初始化逻辑,将TeamCodec实例注册到MongoDB的CodecRegistry:@ApplicationScoped public class MongoCodecInitializer { @Inject TeamCodec teamCodec; @Inject MongoClient mongoClient; void init(@Observes StartupEvent event) { CodecRegistry codecRegistry = CodecRegistries.fromRegistries( mongoClient.getCodecRegistry(), CodecRegistries.fromCodecs(teamCodec) ); // 使用该codecRegistry获取数据库/集合 MongoDatabase db = mongoClient.getDatabase("liquido").withCodecRegistry(codecRegistry); } }确认UserService的作用域
UserService使用@Singleton是合理的,只要TeamCodec由CDI管理,就能正常注入UserService。
额外注意事项
- 避免在Codec的
decode方法中执行数据库操作,这可能引发线程安全或性能问题——MongoDB Codec设计初衷是轻量级数据转换,复杂关联逻辑建议放到后续业务流程中处理。
内容的提问来源于stack exchange,提问作者Robert

