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,可以实现:

  • 日期、枚举等特殊类型的格式化
  • 空值过滤、缩进美化等输出控制
  • 复杂对象和集合的序列化
  • 自定义序列化逻辑
相关推荐
西部风情4 分钟前
聊聊并发、在线、TPS
android·java·数据库
爬虫程序猿1 小时前
用 Python 给京东商品详情做“全身 CT”——可量产、可扩展的爬虫实战
开发语言·爬虫·python
顾漂亮2 小时前
Token快过期的三种续期方案
java·spring·状态模式
徐同保3 小时前
tailwindcss暗色主题切换
开发语言·前端·javascript
蓝纹绿茶3 小时前
bash:**:pip:***python: 错误的解释器: 没有那个文件或目录
开发语言·python·pip
云知谷3 小时前
【经典书籍】C++ Primer 第15章类虚函数与多态 “友元、异常和其他高级特性” 精华讲解
c语言·开发语言·c++·软件工程·团队开发
START_GAME4 小时前
深度学习Diffusers:用 DiffusionPipeline 实现图像生成
开发语言·python·深度学习
牢七4 小时前
mwf攻防。
java
不爱编程的小九九4 小时前
小九源码-springboot088-宾馆客房管理系统
java·开发语言·spring boot
Evand J5 小时前
【MATLAB例程】到达角度定位(AOA),平面环境多锚点定位(自适应基站数量),动态轨迹使用EKF滤波优化。附代码下载链接
开发语言·matlab·平面·滤波·aoa·到达角度