You need to enable JavaScript to run this app. 如何为Spring Boot Controller配置不同ObjectMapper实现差异化响应序列化
优惠活动
大模型
产品
解决方案
定价
更多

如何为Spring Boot Controller配置不同ObjectMapper实现差异化响应序列化

Spring Boot 按Controller粒度差异化配置响应序列化ObjectMapper实现

现有配置现状

项目当前已在Spring容器中注册了@Primary修饰的全局单例ObjectMapper,作为所有REST Controller的默认序列化组件,简化配置如下:

@Bean
@Primary
public ObjectMapper objectMapper() {
    return new ObjectMapper()
        .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}

受Spring MVC默认加载逻辑影响,所有接口的响应序列化都会复用这同一个单例实例,无法直接匹配不同Controller的输出规则差异。

预期配置目标

需要实现Controller维度的序列化规则绑定,具体匹配关系如下:

  • Controller A、B、C 使用专属ObjectMapper X:序列化时自动忽略所有值为null的字段
  • Controller D 使用专属ObjectMapper Y:序列化时自动忽略所有空值字段(覆盖null、空字符串、空集合等场景)
  • 其余未做特殊配置的Controller 沿用全局默认的ObjectMapper Z:序列化时完整返回包含null值的全部字段

实现约束

  • 无法修改项目内自动生成的POJO类,也不打算调整代码生成库逻辑、修改对应Mustache模板
  • 不采用自定义MediaType的内容协商方案实现序列化逻辑的差异化

符合约束的落地实现方案

整个实现完全基于Spring Web提供的原生扩展点完成,不侵入业务代码、不修改POJO结构,具体分为三步:

1. 拆分注册不同配置的ObjectMapper实例

保留原全局@Primary的ObjectMapper作为默认逻辑,再基于全局实例拷贝生成两个专属配置的实例,避免通用配置丢失:

// 全局默认ObjectMapper Z:保留原配置,序列化时不忽略null值
@Bean
@Primary
public ObjectMapper objectMapperZ() {
    return new ObjectMapper()
        .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}

// 专属ObjectMapper X:序列化时忽略null值
@Bean
public ObjectMapper objectMapperX() {
    // 从全局实例拷贝生成,保留所有通用配置
    return objectMapperZ().copy()
        .setSerializationInclusion(JsonInclude.Include.NON_NULL);
}

// 专属ObjectMapper Y:序列化时忽略空值(null、空字符串、空集合等)
@Bean
public ObjectMapper objectMapperY() {
    return objectMapperZ().copy()
        .setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
}

注意:所有自定义ObjectMapper都必须通过全局实例的copy()方法生成,不能直接new新实例,否则会丢失全局配置中的反序列化规则、Jackson模块注册等通用逻辑。

2. 定义Controller匹配标识

创建仅作用于类级别的自定义注解,用来标记需要绑定专属序列化规则的Controller:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface JsonObjectMapper {
    // 填写要使用的ObjectMapper对应的Bean名称
    String value();
}

后续直接在需要特殊规则的Controller类上标注即可,无需修改其他逻辑:

  • Controller A、B、C 类上添加 @JsonObjectMapper("objectMapperX")
  • Controller D 类上添加 @JsonObjectMapper("objectMapperY")
  • 其他Controller不做任何标注,自动走全局默认的ObjectMapper Z逻辑

3. 实现序列化规则的动态切换

利用Spring MVC提供的ResponseBodyAdvice扩展点,在响应写出阶段根据Controller上的注解动态绑定对应的ObjectMapper,完全不侵入原有业务代码:

@ControllerAdvice
public class DynamicJsonSerializeAdvice implements ResponseBodyAdvice<Object> {
    private final Map<String, ObjectMapper> allObjectMappers;
    // 自动注入容器中所有ObjectMapper实例,按Bean名称匹配
    public DynamicJsonSerializeAdvice(Map<String, ObjectMapper> allObjectMappers) {
        this.allObjectMappers = allObjectMappers;
    }

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 仅对标注了自定义注解的Controller生效
        return returnType.getContainingClass().isAnnotationPresent(JsonObjectMapper.class);
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, 
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType, 
                                  ServerHttpRequest request, ServerHttpResponse response) {
        // 读取Controller上的注解配置,匹配对应的目标ObjectMapper
        JsonObjectMapper config = returnType.getContainingClass().getAnnotation(JsonObjectMapper.class);
        ObjectMapper targetMapper = allObjectMappers.get(config.value());
        if (targetMapper == null) {
            // 找不到对应配置时直接走默认序列化逻辑
            return body;
        }
        // 包装响应体,绑定指定的ObjectMapper完成后续序列化流程
        MappingJacksonValue wrapper = new MappingJacksonValue(body);
        wrapper.setObjectMapper(targetMapper);
        return wrapper;
    }
}

内容的提问来源于stack exchange,提问作者rmrt

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.29 08:54:01