ObjectMapper 在 Spring 统一响应处理中的作用详解

ObjectMapper 是 Jackson 库的核心类,专门用于处理 JSON 数据的序列化(Java 对象 → JSON)和反序列化(JSON → Java 对象)。在你提供的代码中,它解决了字符串响应特殊处理的关键问题。

一、为什么需要 ObjectMapper?

问题背景

java 复制代码
if (body instanceof String) {
    return mapper.writeValueAsString(Result.success(body));
}

这段代码处理的是当控制器返回纯字符串时的特殊情况。在 Spring MVC 中,不同类型的返回值有不同的处理方式:

返回值类型 处理方式 问题
对象/集合 自动使用 JSON 转换器
字符串 使用字符串转换器 无法自动包装为 JSON

具体问题演示

假设有一个控制器:

java 复制代码
@RestController
public class ExampleController {
    @GetMapping("/string")
    public String getString() {
        return "hello"; // 返回纯字符串
    }
}

没有 ObjectMapper 的情况

  1. beforeBodyWrite 返回 Result.success("hello")
  2. Spring 尝试使用 StringHttpMessageConverter 处理
  3. 因为 Result 不是字符串 → 类型转换错误!

二、ObjectMapper 如何解决这个问题

解决方案

java 复制代码
mapper.writeValueAsString(Result.success(body));

分步解析

java 复制代码
创建包装对象:

Result result = Result.success("hello");
// 得到:{code:200, msg:"success", data:"hello"}
手动序列化为 JSON 字符串:

String json = mapper.writeValueAsString(result);
// 得到:'{"code":200,"msg":"success","data":"hello"}'
  1. 最终返回字符串结果

    • 符合 StringHttpMessageConverter 的预期
    • 客户端收到标准 JSON 格式

序列化过程图解

java 复制代码
Java对象: Result
   ├─ code: 200
   ├─ msg: "success"
   └─ data: "hello"
       ↓ ObjectMapper 序列化
JSON字符串: '{"code":200,"msg":"success","data":"hello"}'

三、ObjectMapper 的核心能力

1. 序列化配置

java 复制代码
// 创建时可配置(代码中通常静态初始化)
private static ObjectMapper mapper = new ObjectMapper();

// 常用配置(可添加到静态初始化块)
static {
    // 美化输出(开发环境)
    mapper.enable(SerializationFeature.INDENT_OUTPUT);
    
    // 空值不参与序列化
    mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    
    // 日期格式标准化
    mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}

2.支持复杂类型

java 复制代码
// 嵌套对象
Result result = Result.success(new User("Alice", 25));
String json = mapper.writeValueAsString(result);
// 输出:{"code":200,"msg":"success","data":{"name":"Alice","age":25}}

// 集合类型
List<String> list = Arrays.asList("A", "B", "C");
String json = mapper.writeValueAsString(Result.success(list));
// 输出:{"code":200,"msg":"success","data":["A","B","C"]}

3. 自定义序列化(高级)

java 复制代码
// 自定义序列化器
public class MoneySerializer extends StdSerializer<BigDecimal> {
    public MoneySerializer() {
        super(BigDecimal.class);
    }
    
    @Override
    public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider provider) {
        gen.writeString(value.setScale(2) + "元");
    }
}

// 注册自定义序列化器
mapper.registerModule(new SimpleModule()
    .addSerializer(BigDecimal.class, new MoneySerializer()));

// 使用效果
Result result = Result.success(new BigDecimal("123.456"));
String json = mapper.writeValueAsString(result);
// 输出:{"code":200,"msg":"success","data":"123.46元"}

四、实际应用场景

场景 1:统一处理日期格式

java 复制代码
static {
    mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
}

// 控制器返回
@GetMapping("/date")
public Date getDate() {
    return new Date(); // 返回日期对象
}

// 处理结果
// 原始输出:1689987600000(时间戳)
// 处理后输出:"2023-07-22"

场景 2:处理枚举类型

java 复制代码
public enum Status {
    ACTIVE, INACTIVE
}

// 默认序列化
Status.ACTIVE → "ACTIVE"(枚举名)

// 自定义序列化
mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);

// 在枚举中添加toString
public enum Status {
    ACTIVE("激活"), INACTIVE("禁用");
    
    private String desc;
    
    @Override
    public String toString() {
        return desc;
    }
}

// 输出结果:"激活"

场景 3:处理特殊字符

java 复制代码
String text = "包含<特殊>字符&符号";
Result result = Result.success(text);

// 未处理时:可能破坏JSON结构
// 处理后:自动转义为"包含\u003C特殊\u003E字符\u0026符号"

完整的最佳实践

java 复制代码
@Slf4j
@ControllerAdvice
public class GlobalResponseAdvice implements ResponseBodyAdvice<Object> {

    // 静态初始化(线程安全)
    private static final ObjectMapper mapper = new ObjectMapper();
    
    static {
        // 基础配置
        mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        
        // 日期格式
        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        
        // 注册Java 8时间模块
        mapper.registerModule(new JavaTimeModule());
    }

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        // 排除特定注解或类型
        return !returnType.hasMethodAnnotation(IgnoreWrap.class);
    }

    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, 
                                  MediaType selectedContentType, Class selectedConverterType, 
                                  ServerHttpRequest request, ServerHttpResponse response) {
        
        // 1. 已经是包装类型则直接返回
        if (body instanceof Result) {
            return body;
        }
        
        // 2. 处理空响应
        if (body == null) {
            return Result.success();
        }
        
        // 3. 特殊处理String类型
        if (body instanceof String) {
            // 设置正确的Content-Type
            response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
            return mapper.writeValueAsString(Result.success(body));
        }
        
        // 4. 添加请求ID到响应头
        String requestId = request.getHeaders().getFirst("X-Request-ID");
        if (requestId != null) {
            response.getHeaders().add("X-Request-ID", requestId);
        }
        
        // 5. 默认包装
        return Result.success(body);
    }
}

总结

ObjectMapper 在统一响应处理中扮演着JSON 序列化引擎的角色,核心解决了两个关键问题:

  1. 统一响应格式:将各种类型的数据包装为标准结构
  2. 特殊类型处理:解决字符串返回值无法自动包装的问题

通过合理配置 ObjectMapper,可以实现:

  • 日期、枚举等特殊类型的格式化
  • 空值过滤、缩进美化等输出控制
  • 复杂对象和集合的序列化
  • 自定义序列化逻辑
相关推荐
有梦想的攻城狮39 分钟前
maven中的maven-antrun-plugin插件详解
java·maven·插件·antrun
_r0bin_3 小时前
前端面试准备-7
开发语言·前端·javascript·fetch·跨域·class
zhang98800003 小时前
JavaScript 核心原理深度解析-不停留于表面的VUE等的使用!
开发语言·javascript·vue.js
硅的褶皱4 小时前
对比分析LinkedBlockingQueue和SynchronousQueue
java·并发编程
MoFe14 小时前
【.net core】天地图坐标转换为高德地图坐标(WGS84 坐标转 GCJ02 坐标)
java·前端·.netcore
季鸢5 小时前
Java设计模式之观察者模式详解
java·观察者模式·设计模式
Fanxt_Ja5 小时前
【JVM】三色标记法原理
java·开发语言·jvm·算法
蓝婷儿5 小时前
6个月Python学习计划 Day 15 - 函数式编程、高阶函数、生成器/迭代器
开发语言·python·学习
love530love5 小时前
【笔记】在 MSYS2(MINGW64)中正确安装 Rust
运维·开发语言·人工智能·windows·笔记·python·rust
Mr Aokey6 小时前
Spring MVC参数绑定终极手册:单&多参/对象/集合/JSON/文件上传精讲
java·后端·spring