@DateTimeFormat 与 @JsonFormat 详解

@DateTimeFormat 与 @JsonFormat 详解

@DateTimeFormat 与 @JsonFormat 详解

适用场景:Spring Boot + Jackson 项目中的时间类型参数处理


一、概述

在 Spring Boot 项目中,处理时间类型(LocalDateTimeDate等)时,开发者常常遇到两个注解:

  • @DateTimeFormat:来自 Spring 框架
  • @JsonFormat:来自 Jackson 框架

两者职责不同、作用时机不同,混用或单用都可能导致时间解析失败。本文将系统梳理两者的区别与最佳实践。


二、@DateTimeFormat — 入参解析印

2.1 基本信息

属性说明
归属org.springframework.format.annotation
框架Spring MVC
职责将 HTTP 请求入参中的字符串 → 时间对象

2.2 适用场景

场景是否生效
@RequestParam查询参数✅ 生效
@ModelAttribute表单提交✅ 生效
实体类字段(Form 提交)✅ 生效
@RequestBodyJSON 字段不生效(由 Jackson 处理)

2.3 使用示例

// GET /user/list?createTime=2024-01-15 10:30:00@GetMapping("/list")publicApiResult<List<UserVO>>list(@RequestParam@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")LocalDateTimecreateTime){// ...}
@DatapublicclassUserQueryDTO{// Form 表单提交时,Spring MVC 会按此格式解析字符串@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")privateLocalDateTimestartTime;}

三、@JsonFormat — JSON 序列化/反序列化印

3.1 基本信息

属性说明
归属com.fasterxml.jackson.annotation
框架Jackson
职责控制 JSON序列化(对象 → 字符串)与反序列化(字符串 → 对象)的格式

3.2 适用场景

场景是否生效
@RequestBodyJSON 反序列化✅ 生效
@ResponseBodyJSON 序列化(响应输出)✅ 生效
@RequestParam查询参数不生效
Form 表单提交不生效

3.3 使用示例

@DatapublicclassUserVO{// 序列化输出 + JSON 反序列化输入,均按此格式处理@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="Asia/Shanghai")privateLocalDateTimecreateTime;}

3.4 重要参数说明

参数说明示例
pattern时间格式"yyyy-MM-dd HH:mm:ss"
timezone时区(不指定可能导致时间偏移 8 小时)"Asia/Shanghai"/"GMT+8"
shape序列化形状JsonFormat.Shape.STRING

四、核心对比

维度@DateTimeFormat@JsonFormat
归属框架Spring MVCJackson
作用方向入参解析(String → Date)序列化 + 反序列化
适用场景Form / Query 参数JSON Body 入参 + 响应输出
时区参数❌ 无timezone
生效位置方法参数、字段字段、getter/setter

五、最佳实践:双印加持

对于实体类/DTO 字段,同时加上两个注解,覆盖所有入参场景:

@DatapublicclassProjectPageDTO{/** * 开始时间 * - @DateTimeFormat:处理 Query/Form 入参(Spring MVC 解析) * - @JsonFormat:处理 JSON Body 入参 + 响应输出(Jackson 处理) */@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="Asia/Shanghai")privateLocalDateTimestartTime;@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="Asia/Shanghai")privateLocalDateTimeendTime;}

六、全局配置(更优解)

在 Spring Boot 项目中,可通过全局 Jackson 配置统一处理序列化格式,避免在每个字段上重复添加@JsonFormat

6.1 全局 Jackson 配置

@ConfigurationpublicclassJacksonConfig{@BeanpublicJackson2ObjectMapperBuilderCustomizerjsonCustomizer(){returnbuilder->{// 全局时区builder.timeZone(TimeZone.getTimeZone("Asia/Shanghai"));// LocalDateTime 序列化格式builder.serializers(newLocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));// LocalDateTime 反序列化格式builder.deserializers(newLocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));};}}

6.2 全局配置后的使用策略

场景是否需要注解
普通时间字段,格式与全局一致❌ 无需添加@JsonFormat
特殊格式字段(如只需日期yyyy-MM-dd✅ 需要单独加@JsonFormat覆盖
Query 参数(所有场景)✅ 仍需手动加@DateTimeFormat

七、常见陷阱

陷阱一:仅加 @JsonFormat,Query 参数报错

// ❌ GET 请求传参时解析失败@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")privateLocalDateTimecreateTime;

错误信息Failed to convert value of type 'String' to required type 'LocalDateTime'

陷阱二:仅加 @DateTimeFormat,JSON Body 格式不受控

// ❌ JSON 入参和响应依赖全局 Jackson 配置,字段级别无法控制格式@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")privateLocalDateTimecreateTime;

陷阱三:@JsonFormat 未指定 timezone 导致时间偏移

// ❌ 可能导致时间偏移 8 小时(UTC vs Asia/Shanghai)@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")privateLocalDateTimecreateTime;// ✅ 明确指定时区@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="Asia/Shanghai")privateLocalDateTimecreateTime;

陷阱四:两个注解的 pattern 不一致

// ❌ 格式不统一,不同入参方式解析结果不同,难以排查@DateTimeFormat(pattern="yyyy-MM-dd")@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="Asia/Shanghai")privateLocalDateTimecreateTime;

八、完整代码示例

以下是一个涵盖各场景的完整示例:

/** * 项目分页查询 DTO */@DatapublicclassProjectPageDTO{/** 项目名称(模糊搜索) */privateStringprojectName;/** * 创建时间起(双印加持) * Query 参数 + JSON Body 均支持 */@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="Asia/Shanghai")privateLocalDateTimecreateTimeStart;/** * 创建时间止 */@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="Asia/Shanghai")privateLocalDateTimecreateTimeEnd;}
/** * 项目响应 VO */@DatapublicclassProjectVO{privateLongprojectId;privateStringprojectName;/** * 创建时间 * 全局 JacksonConfig 已配置默认格式时,此注解可省略 * 需要特殊格式时才单独加 */@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="Asia/Shanghai")privateLocalDateTimecreateTime;}

九、总结

HTTP 请求 │ ┌─────────┴─────────┐ │ │ Query/Form JSON Body │ │ @DateTimeFormat @JsonFormat (Spring MVC 解析) (Jackson 解析) │ │ └─────────┬─────────┘ │ 时间对象 ✅
注解核心职责一句话记忆
@DateTimeFormatQuery/Form 入参解析“URL 和表单用我”
@JsonFormatJSON 序列化/反序列化“JSON 进出用我”

最佳实践

  1. 全局配置JacksonConfig统一 JSON 时间格式与时区
  2. 所有时间类型字段标注@DateTimeFormat,覆盖 Query 参数场景
  3. 仅在需要特殊格式时,才在字段上单独加@JsonFormat覆盖全局配置

本文基于 Spring Boot 2.7.x + Jackson 2.13.x 编写,适用于 Java 8+ 项目。