Spring MVC 整合 Jackson 实现 JSON 转换
HttpMessageConverter 机制完成这一集成,其具体实现由 MappingJackson2HttpMessageConverter 负责。
核心机制解析
组件协作关系
整个转换体系涉及三个层级:
- HttpMessageConverter:Spring 定义的消息转换契约,用于桥接 HTTP 报文体与 Java 类型
- MappingJackson2HttpMessageConverter:基于 Jackson 的具体实现,封装了
ObjectMapper的调用逻辑 - ObjectMapper:Jackson 库的心脏,执行真正的编解码运算
响应阶段的数据流转
当接口需要返回 JSON 时,处理链路如下:
- 处理器方法返回 POJO 实例
- Spring 依据请求头
Accept: application/json筛选匹配的转换器 MappingJackson2HttpMessageConverter被选中并触发write()操作- 内部委托
ObjectMapper.writeValueAsBytes()完成字节流输出
请求阶段的解析过程
接收 JSON 载荷时,@RequestBody 标注的参数会驱动 ObjectMapper.readValue() 执行反序列化,将字节数据还原为对象实例。
工程依赖引入
通过 Maven 引入核心库:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.3</version>
</dependency>
采用 Spring Boot 时,spring-boot-starter-web 启动器已内嵌相关依赖,无需额外声明。
实践代码演示
服务端接口定义
@RestController
@RequestMapping("/api/customers")
public class CustomerController {
@GetMapping("/{customerId}")
public Customer fetchById(@PathVariable Long customerId) {
return new Customer(customerId, "李明", 28);
}
@PostMapping
public ResponseEntity<String> register(@RequestBody Customer payload) {
// 执行业务持久化
return ResponseEntity.status(HttpStatus.CREATED)
.body("注册完成: " + payload.getNickname());
}
}
领域模型设计
public class Customer {
private Long uid;
private String nickname;
private Integer yearsOld;
public Customer(Long uid, String nickname, Integer yearsOld) {
this.uid = uid;
this.nickname = nickname;
this.yearsOld = yearsOld;
}
// 访问器方法省略...
}
Jackson 的默认策略会采集所有公有属性以及具备 getter 方法的私有字段。
注解精细化操控
| 注解 | 功能说明 |
|---|---|
@JsonIgnore | 排除字段不参与任何方向的转换 |
@JsonAlias | 指定反序列化时的兼容字段名 |
@JsonProperty("new_key") | 重命名输出键 |
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") | 控制时间型数据的输出格式 |
@JsonInclude(JsonInclude.Include.NON_ABSENT) | 按条件忽略字段 |
标注示例
public class Customer {
private Long uid;
@JsonProperty("display_name")
private String nickname;
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "Asia/Shanghai")
private LocalDate birthDay;
@JsonIgnore
private String credential;
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
private Integer score = 0;
// ...
}
全局行为定制方案
配置属性方式(YAML)
spring:
jackson:
default-property-inclusion: non_null
serialization:
write-dates-as-timestamps: false
fail-on-empty-beans: false
deserialization:
fail-on-unknown-properties: false
date-format: yyyy-MM-dd HH:mm:ss
time-zone: Asia/Shanghai
编程式配置
@Configuration
public class ObjectMapperCustomization {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper om = new ObjectMapper();
om.setSerializationInclusion(JsonInclude.Include.NON_NULL);
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
om.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
om.enable(SerializationFeature.INDENT_OUTPUT);
// 扩展 JDK8 日期时间支持
om.registerModule(new JavaTimeModule());
return om;
}
}
深度定制转换器
在需要完全接管 JSON 处理逻辑的场景下,可手工装配消息转换器:
@Configuration
public class MessageConversionConfiguration {
@Bean
public MappingJackson2HttpMessageConverter tailoredConverter() {
MappingJackson2HttpMessageConverter converter =
new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(tailoredMapper());
// 限定支持的媒体类型与字符集
List<MediaType> supported = Arrays.asList(
MediaType.APPLICATION_JSON,
new MediaType("application", "*+json")
);
converter.setSupportedMediaTypes(supported);
return converter;
}
private ObjectMapper tailoredMapper() {
ObjectMapper om = new ObjectMapper();
// 注入特定模块或特性...
return om;
}
}
典型问题排查
| 现象 | 根因与对策 |
|---|---|
LocalDateTime 输出为数字数组 | 关闭 WRITE_DATES_AS_TIMESTAMPS 并确保 JavaTimeModule 已加载 |
| 空值字段残留于 JSON | 全局设置 NON_NULL 或在类/字段级应用 @JsonInclude |
| 反序列化抛出异常 | 确认目标类具备无参构造器,或采用 @JsonCreator 标注工厂方法 |
| 未知字段导致失败 | 禁用 DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES |