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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 21:37:35