Quarkus中如何在JsonSerializer中实现非静态CDI注入
在Jackson的@JsonSerialize指定的序列化器中注入CDI依赖(非全局注册方案)
直接给ThingSerializer添加@ApplicationScoped和@Inject无法生效的原因很明确:Jackson处理@JsonSerialize(using=...)时,会通过无参构造器直接实例化序列化器,不会走CDI的Bean创建流程,因此CDI的依赖注入逻辑不会被触发,最终someService会是null。
下面提供两种优雅的非全局注册方案,都不需要使用CDI.current()静态调用:
方案一:实现ContextualSerializer接口,按需获取依赖
这个方案的核心是利用Jackson的ContextualSerializer接口,在序列化器被实际使用时,动态获取CDI依赖并创建带依赖的序列化器实例,完全不需要全局注册序列化器。
步骤1:修改ThingSerializer实现ContextualSerializer
public class ThingSerializer extends StdSerializer<Thing> implements ContextualSerializer { private final SomeService someService; // 无参构造,供Jackson初始调用 public ThingSerializer() { super(Thing.class); this.someService = null; } // 带依赖的私有构造,供上下文创建时使用 private ThingSerializer(SomeService someService) { super(Thing.class); this.someService = someService; } @Override public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException { // 从JNDI获取BeanManager(Jakarta EE/Quarkus等容器均支持此方式) try { BeanManager beanManager = (BeanManager) new InitialContext().lookup("java:comp/BeanManager"); // 获取SomeService的CDI实例 SomeService service = beanManager.createInstance().select(SomeService.class).get(); // 返回带依赖的序列化器实例 return new ThingSerializer(service); } catch (NamingException e) { throw new JsonMappingException("无法获取BeanManager以注入依赖", e); } } @Override public void serialize(Thing thing, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { // 这里可以正常使用someService String processedValue = someService.processThing(thing); jsonGenerator.writeString(processedValue); } }
优势
- 仅在
@JsonSerialize(using=ThingSerializer.class)被触发时才获取依赖,完全不影响全局序列化逻辑 - 不需要修改
ObjectMapper的全局配置 - 避免了静态的CDI调用
方案二:利用Jackson的InjectableValues注入依赖
通过给CDI管理的ObjectMapper配置InjectableValues,将需要的依赖注入到序列化器中,同样不需要全局注册序列化器。
步骤1:配置ObjectMapperCustomizer注入依赖
@ApplicationScoped public class CustomObjectMapperConfig implements ObjectMapperCustomizer { @Inject private SomeService someService; @Override public void customize(ObjectMapper objectMapper) { // 给ObjectMapper设置可注入的依赖 objectMapper.setInjectableValues(new InjectableValues.Std() .addValue(SomeService.class, someService)); } }
步骤2:修改ThingSerializer使用@JacksonInject注入依赖
public class ThingSerializer extends StdSerializer<Thing> { private final SomeService someService; // 通过JacksonInject注入依赖 @JacksonInject public ThingSerializer(SomeService someService) { super(Thing.class); this.someService = someService; } @Override public void serialize(Thing thing, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { // 正常使用someService jsonGenerator.writeString(someService.processThing(thing)); } }
优势
- 代码更简洁,依赖注入逻辑更直观
- 同样仅在使用该序列化器时才会注入依赖,不干扰全局默认序列化
注意事项
- 两种方案都要求运行环境是支持CDI的Jakarta EE/Quarkus/Spring Boot(Spring Boot中需调整BeanManager获取方式,比如用
ApplicationContext) - 方案一中的JNDI查找
java:comp/BeanManager是标准Jakarta EE方式,在大多数容器中都有效
内容的提问来源于stack exchange,提问作者Werner de Groot
相关产品推荐
相关产品推荐

