日期格式化时注解@DateTimeFormat无效的问题分析
作者:汤圆
个人博客:javalover.cc
背景
有时候我们在写接口时,需要把前台传来的日期String类型转为Date类型
这时我们可能会用到@DateTimeFormat注解
在请求数据为非JSON格式时,这个注解是没有问题的,可用的;
但是当请求数据为JSON格式时,问题就出现了
- 此时如果请求参数没有加@RequestBody注解,那么请求参数不会执行类型转换操作,数据都是默认为空(基本类型比如int = 0, 对象引用比如Date date= null)
- 此时如果请求参数有加@RequestBody注解,那么请求参数会执行JSON类型转换操作,但是转换会提示异常
所以文章题目中所说的有时无效,指的就是上面这两种情况
目录
本文分三步走,如下所示,其中会穿插着介绍@DateTimeFormat、@RequestBody、@JsonFormat注解

分析
1. 基础代码:
AnnationApplication.java:主程序兼控制器
package com.jalon.annation;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class AnnationApplication {
public static void main(String[] args) {
SpringApplication.run(AnnationApplication.class, args);
}
@PostMapping("/personPost")
public Person personPost(Person person){
System.out.println(person);
return person;
}
}
Person.java 实体类
package com.jalon.annation;
import com.fasterxml.jackson.annotation.JacksonAnnotation;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Component;
import java.util.Date;
public class Person {
private int age;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date birth;
@Override
public String toString() {
return "Person{" +
"age=" + age +
", birth=" + birth +
'}';
}
// 省略getter/setter
}
2. 案例分析:
这里我们用的是PostMan进行测试,请求示例如下
所有示例全程都有@DateTimeFormat注解
示例1:
请求方式:Post请求
数据格式:非JSON格式,比如form-data
请求资源:personPost(Person person),无@RequestBody注解
具体请求内容和返回结果如下所示

可以看到,前台返回正常(数据无误),说明@DateTimeFormat有效,成功解析了日期字符串
这里返回的数据都是经过@ResponseBody处理过的,因为我们没有配置返回数据的日期格式化,所以这里返回的日期格式是默认的
@ResponseBody对应于@RequestBody;
- 前者负责将Java对象序列号成JSON数据进行返回
- 后者负责解析请求过来的JSON数据,解析成对应的Java对象
我们再来看下后台,打印如下:
Person{age=1, birth=Wed Jan 01 00:00:00 CST 2020}
可以看到,后台打印正常(数据无误,日期格式忽略,因为这里的date.toString用的Date的默认方法)
从上面的结果我们可以看到,@DateTimeFormat只是负责解析传来的日期字符串,转为对应的日期对象;
但是并不会修改原有的日期对象的格式(从前台返回和后台输出可以看到,日期格式不受@DateTimeFormat的影响)
示例2:
请求方式:Post请求
数据格式:JSON格式,比如application/json
请求资源:personPost(Person person),无@RequestBody注解
具体请求内容和返回结果如下所示

可以看到,返回数据都为空(默认的初始值),说明数据都没有传过去,不止是date,连基本类型int都没过去
我们再来看下后台,打印如下
Person{age=0, birth=null} // 跟前台返回的数据一致
可以看到,后台解析到的数据也是空的,所以上面返回的当然是空的
原因就是默认的类型转换器是没有转化成JSON格式的对应转换类的,部分转换器如下所示,(core.convert.support包)

解决:所以这里对应的解决办法就是,自己创建一个JSON转换器
但是实际上这个已经有实现了,只是没有触发,如下所示的构建工具(http.converter.json包),就是用来配置相关的json序列化和反序列化的

现在我们可以通过@RequestBody注解来触发,它在接收到JSON格式的数据时,会自动调用对应的JSON转换器
下面的示例3就是这个例子
加了@RequestBody后,默认只接受application/json格式的数据,如果传入其他格式,会报415不支持的类型
示例3:
请求方式:Post请求
数据格式:JSON格式,比如application/json
请求资源:personPost(@RequestBody Person person),有@RequestBody注解
具体请求内容和返回结果如下所示

可以看到,报错了,提示400,这种一般属于客户端错误(比如数据格式不正确,数据过大等)
我们再来看下后台,打印如下
2021-05-15 13:48:41.578 WARN 38426 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.util.Date` from String "2020-01-01 00:00:00": not a valid representation (error: Failed to parse Date value '2020-01-01 00:00:00': Cannot parse date "2020-01-01 00:00:00": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSX', parsing fails (leniency? null)); nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String "2020-01-01 00:00:00": not a valid representation (error: Failed to parse Date value '2020-01-01 00:00:00': Cannot parse date "2020-01-01 00:00:00": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSX', parsing fails (leniency? null))
at [Source: (PushbackInputStream); line: 3, column: 14] (through reference chain: com.jalon.annation.Person["birth"])]
这里我们提取关键的部分来看:
1. nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String "2020-01-01 00:00:00"
2. Cannot parse date "2020-01-01 00:00:00": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSX'
首先这里跟示例2不同,这里起码做了尝试转换,只是没有找到对应的格式,所以转换失败了
可以看到,它并没有按照上面我们的@DateTimeFormat注解去解析,而是按照''yyyy-MM-dd'T'HH:mm:ss.SSSX"这个格式去解析
这里如果想投机的话,可以在前台直接传入''yyyy-MM-dd'T'HH:mm:ss.SSSX'格式的数据,如下:

但是这种办法对于前端很不友好(极其不好)
所以下面还是给出正常的解决办法
解决:所以这里的解决办法就是自己定义日期格式
- 方案一:局部注解来解决,比如在date字段添加@JsonFormat()注解
// 这个注解用来解析JSON数据中的日期字符串,会序列化返回数据
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date birth;
局部的特点:灵活,但是配置繁琐,不统一(每个字段都要加)
- 方案二:全局配置来解决,比如配置一个Jackson2ObjectMapperBuilderCustomizer,然后自定义日期反序列化格式
package com.jalon.annation;
import com.fasterxml.jackson.databind.deser.std.DateDeserializers;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.text.SimpleDateFormat;
import java.util.Date;
@Configuration
public class MyDateConvertCustoms implements Jackson2ObjectMapperBuilderCustomizer {
@Override
public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
// 覆盖默认的Date反序列化,第一个参数为需要反序列化的类,第二个为具体的序列化格式
jacksonObjectMapperBuilder.deserializerByType(
Date.class
,new DateDeserializers.DateDeserializer(
DateDeserializers.DateDeserializer.instance
, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
, null));
}
}
全局的特点:不灵活,但是直观清晰,配置统一
3. 结论分析:
主要根据请求的数据类型来对比
- 请求非JSON数据,建议用@DateTimeFormat即可(比如get请求,当然get请求也可以请求JSON数据,只是不推荐)
- 请求JSON数据,建议用@ReqeustBody来转换数据,然后搭配局部注解@JsonFormat或者全局配置来修改默认的日期解析格式(默认"yyyy-MM-dd'T'HH:mm:ss.SSSX")
总结
注解相关:
@DateTimeFormat注解:适用于请求数据为非JSON数据,不会格式化返回数据@JsonFormat注解:适用于请求数据为JSON数据(尤其有日期数据时),且需在请求方法的参数前加@RequestBody`注解,会格式化返回数据@RequestBody注解:解析传来的JSON数据,转换成对应的Java对象@ResponseBody注解:转换Java对象为JSON数据,用来作为返回数据输出到前端
日期格式化相关:
请求非JSON数据,建议用@DateTimeFormat即可,此时不会格式化返回数据(比如get请求,当然get请求也可以请求JSON数据,只是不推荐)
请求JSON数据,建议用@ReqeustBody来转换数据,然后搭配局部注解@JsonFormat(会格式化返回数据)或者全局配置来修改默认的日期解析格式(默认"yyyy-MM-dd'T'HH:mm:ss.SSSX");全局配置也可以格式化返回数据,需配置builder.serializerByType
如果日期格式化出错,先看传来的数据是否为JSON数据(可以通过consumes来限制),然后再看有没有对于的注解或日期格式化全局配置
参考内容:
- @RequestBody: https://blog.csdn.net/justry_deng/article/details/80972817/
- @DateTimeFormat: https://segmentfault.com/a/1190000020423352
后记
学习之路漫漫,共勉之
写在最后:
愿你的意中人亦是中意你之人
日期格式化时注解@DateTimeFormat无效的问题分析的更多相关文章
- shell日期格式化、加减运算
#!/bin/bash echo i love you输出:i love you =======================================反引号的作用============== ...
- SpringMVC返回JSON数据时日期格式化问题
https://dannywei.iteye.com/blog/2022929 SpringMVC返回JSON数据时日期格式化问题 博客分类: Spring 在运用SpringMVC框架开发时,可 ...
- springMvc 注解@JsonFormat 日期格式化
1:一定要加入依赖,否则不生效: <!--日期格式化依赖--> <dependency> <groupId>com.fasterxml.jackson.core&l ...
- spingmvc 返回json数据日期格式化方法
第一种: json 用的是这个依赖 <!-- JSON lib 开发包 以及它的依赖包 --> <dependency> <groupId>com.fasterxm ...
- springMVC框架返回JSON到前端日期格式化
在Controller类中,需要返回JSON的方法上加上注释@ResponseBody,这是必须的. 然后spring-servlet.xml配置如下: <?xml version=" ...
- 分享自己写的JS版日期格式化和解析工具类,绝对好用!
前言 本来想模仿Java里面的SimpleDateFormat()对象的,但是感觉这样用起来不方便,所以还是直接写成单独的方法算了. 原文链接 日期格式化 使用说明 formatDate(date, ...
- JS 日期格式化和解析工具
本来想模仿Java里面的SimpleDateFormat()对象的,但是感觉这样用起来不方便,所以还是直接写成单独的方法算了. 原文链接 日期格式化 使用说明 formatDate(date, fmt ...
- JS版日期格式化和解析工具类,毫秒级
/** * ===================================== * 日期相关方法 * ===================================== */ ;(fu ...
- springmvc日期格式化
jsp页面String类型转Controller后台Date类型 方法1.在实体中加入日期格式化注解 @DateTimeFormat(pattern="yyyy-MM-dd") p ...
随机推荐
- 2021-2-28 Mark-Java Interview Simple
2021-2-28 Mark-Java Interview Simple 心动时,还没来得及学会勇敢. 勇敢时,我们已在时光里走散. 简介:2021-2-28日Java面试没能脱口而出或是回答得不好的 ...
- 全网最详细的新手入门Mysql命令和基础,小白必看!
MySQL简介 什么是数据库 ? 数据库(Database)是按照数据结构来组织.存储和管理数据的仓库,它产生于距今六十多年前,随着信息技术和市场的发展,特别是二十世纪九十年代以后,数据管理不再仅仅是 ...
- B. 【例题2】雷达装置
B . [ 例 题 2 ] 雷 达 装 置 B. [例题2]雷达装置 B.[例题2]雷达装置 题目解析 求最少所需的雷达数,考虑贪心算法. 以这张图为例.以一个城市为中心,作一个半径为 d d d的圆 ...
- Python函数参数和注解是什么
四种参数 Python函数func定义如下: def func(first, *args, second="Hello World", **kwargs): print(first ...
- (一)Struts2框架概述
一.struts2发展历史 经过很多年发展,Struts1已经成为了高度成熟的框架,但随着时间的发展,Struts1的局限性和缺点不断的暴露出来. 现在Struts已经分化成了两个框架 ...
- addeventlistener回调函数中的黑科技
dom.addEventListener('click',callback/obj){},这里的callback除了传递一个函数之外,还可以传递一个属性带有 HandleEvent 方法的对象obj, ...
- 前端 JS 原生 javascript 和 location.hash 实现一个单页应用的路由 router
开篇日常立个flag-- 前言 最近在做一些应用,类似于单页应用,想实现类似于 Vue 路由的效果. 但是个人 Vue 基础四舍五入约等于无,而且看着 Vue-router 吃力+用不起来(因为我的项 ...
- Leedcode算法专题训练(二分查找)
二分查找实现 非常详细的解释,简单但是细节很重要 https://www.cnblogs.com/kyoner/p/11080078.html 正常实现 Input : [1,2,3,4,5] key ...
- 6.1vector用法
目录 一.用法介绍 二.基本用法 三.PAT A1039 一.用法介绍 vector<typename>name; 按照这样的格式进行定义与书写. 注意定义成双数组的情况要加上空格. ve ...
- Java实现基于朴素贝叶斯的情感词分析
朴素贝叶斯(Naive Bayesian)是一种基于贝叶斯定理和特征条件独立假设的分类方法,它是基于概率论的一种有监督学习方法,被广泛应用于自然语言处理,并在机器学习领域中占据了非常重要的地位.在之前 ...