@DateTimeFormat 与 @JsonFormat 详解

@DateTimeFormat 与 @JsonFormat 详解 DateTimeFormat 与 JsonFormat 详解适用场景Spring Boot Jackson 项目中的时间类型参数处理一、概述在 Spring Boot 项目中处理时间类型LocalDateTime、Date等时开发者常常遇到两个注解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?createTime2024-01-15 10:30:00GetMapping(/list)publicApiResultListUserVOlist(RequestParamDateTimeFormat(patternyyyy-MM-dd HH:mm:ss)LocalDateTimecreateTime){// ...}DatapublicclassUserQueryDTO{// Form 表单提交时Spring MVC 会按此格式解析字符串DateTimeFormat(patternyyyy-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(patternyyyy-MM-dd HH:mm:ss,timezoneAsia/Shanghai)privateLocalDateTimecreateTime;}3.4 重要参数说明参数说明示例pattern时间格式yyyy-MM-dd HH:mm:sstimezone时区不指定可能导致时间偏移 8 小时Asia/Shanghai/GMT8shape序列化形状JsonFormat.Shape.STRING四、核心对比维度DateTimeFormatJsonFormat归属框架Spring MVCJackson作用方向入参解析String → Date序列化 反序列化适用场景Form / Query 参数JSON Body 入参 响应输出时区参数❌ 无✅timezone生效位置方法参数、字段字段、getter/setter五、最佳实践双印加持对于实体类/DTO 字段同时加上两个注解覆盖所有入参场景DatapublicclassProjectPageDTO{/** * 开始时间 * - DateTimeFormat处理 Query/Form 入参Spring MVC 解析 * - JsonFormat处理 JSON Body 入参 响应输出Jackson 处理 */DateTimeFormat(patternyyyy-MM-dd HH:mm:ss)JsonFormat(patternyyyy-MM-dd HH:mm:ss,timezoneAsia/Shanghai)privateLocalDateTimestartTime;DateTimeFormat(patternyyyy-MM-dd HH:mm:ss)JsonFormat(patternyyyy-MM-dd HH:mm:ss,timezoneAsia/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七、常见陷阱陷阱一仅加 JsonFormatQuery 参数报错// ❌ GET 请求传参时解析失败JsonFormat(patternyyyy-MM-dd HH:mm:ss)privateLocalDateTimecreateTime;错误信息Failed to convert value of type String to required type LocalDateTime陷阱二仅加 DateTimeFormatJSON Body 格式不受控// ❌ JSON 入参和响应依赖全局 Jackson 配置字段级别无法控制格式DateTimeFormat(patternyyyy-MM-dd HH:mm:ss)privateLocalDateTimecreateTime;陷阱三JsonFormat 未指定 timezone 导致时间偏移// ❌ 可能导致时间偏移 8 小时UTC vs Asia/ShanghaiJsonFormat(patternyyyy-MM-dd HH:mm:ss)privateLocalDateTimecreateTime;// ✅ 明确指定时区JsonFormat(patternyyyy-MM-dd HH:mm:ss,timezoneAsia/Shanghai)privateLocalDateTimecreateTime;陷阱四两个注解的 pattern 不一致// ❌ 格式不统一不同入参方式解析结果不同难以排查DateTimeFormat(patternyyyy-MM-dd)JsonFormat(patternyyyy-MM-dd HH:mm:ss,timezoneAsia/Shanghai)privateLocalDateTimecreateTime;八、完整代码示例以下是一个涵盖各场景的完整示例/** * 项目分页查询 DTO */DatapublicclassProjectPageDTO{/** 项目名称模糊搜索 */privateStringprojectName;/** * 创建时间起双印加持 * Query 参数 JSON Body 均支持 */DateTimeFormat(patternyyyy-MM-dd HH:mm:ss)JsonFormat(patternyyyy-MM-dd HH:mm:ss,timezoneAsia/Shanghai)privateLocalDateTimecreateTimeStart;/** * 创建时间止 */DateTimeFormat(patternyyyy-MM-dd HH:mm:ss)JsonFormat(patternyyyy-MM-dd HH:mm:ss,timezoneAsia/Shanghai)privateLocalDateTimecreateTimeEnd;}/** * 项目响应 VO */DatapublicclassProjectVO{privateLongprojectId;privateStringprojectName;/** * 创建时间 * 全局 JacksonConfig 已配置默认格式时此注解可省略 * 需要特殊格式时才单独加 */JsonFormat(patternyyyy-MM-dd HH:mm:ss,timezoneAsia/Shanghai)privateLocalDateTimecreateTime;}九、总结HTTP 请求 │ ┌─────────┴─────────┐ │ │ Query/Form JSON Body │ │ DateTimeFormat JsonFormat (Spring MVC 解析) (Jackson 解析) │ │ └─────────┬─────────┘ │ 时间对象 ✅注解核心职责一句话记忆DateTimeFormatQuery/Form 入参解析“URL 和表单用我”JsonFormatJSON 序列化/反序列化“JSON 进出用我”最佳实践全局配置JacksonConfig统一 JSON 时间格式与时区所有时间类型字段标注DateTimeFormat覆盖 Query 参数场景仅在需要特殊格式时才在字段上单独加JsonFormat覆盖全局配置本文基于 Spring Boot 2.7.x Jackson 2.13.x 编写适用于 Java 8 项目。