日期格式化时注解@DateTimeFormat无效的问题分析

日期格式化时注解@DateTimeFormat无效的问题分析

背景

有时候我们在写接口时,需要把前台传来的日期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;@Overridepublic String toString() {return "Person{" +"age=" + age +", birth=" + birth +'}';}// 省略getter/setter
}

2. 案例分析:

这里我们用的是PostMan进行测试,请求示例如下

所有示例全程都有@DateTimeFormat注解

示例1:

  • 请求方式:Post请求

  • 数据格式:非JSON格式,比如form-data

  • 请求资源:personPost(Person person),无@RequestBody注解

    具体请求内容和返回结果如下所示

image-20210515133940137

可以看到,前台返回正常(数据无误),说明@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注解

    具体请求内容和返回结果如下所示

post-json-no@RequestBody

可以看到,返回数据都为空(默认的初始值),说明数据都没有传过去,不止是date,连基本类型int都没过去

我们再来看下后台,打印如下

Person{age=0, birth=null} // 跟前台返回的数据一致

可以看到,后台解析到的数据也是空的,所以上面返回的当然是空的

原因就是默认的类型转换器是没有转化成JSON格式的对应转换类的,部分转换器如下所示,(core.convert.support包)

convert-support-classes

解决:所以这里对应的解决办法就是,自己创建一个JSON转换器

但是实际上这个已经有实现了,只是没有触发,如下所示的构建工具(http.converter.json包),就是用来配置相关的json序列化和反序列化的

convert-json-classes

现在我们可以通过@RequestBody注解来触发,它在接收到JSON格式的数据时,会自动调用对应的JSON转换器

下面的示例3就是这个例子

加了@RequestBody后,默认只接受application/json格式的数据,如果传入其他格式,会报415不支持的类型

示例3:

  • 请求方式:Post请求

  • 数据格式:JSON格式,比如application/json

  • 请求资源:personPost(@RequestBody Person person),有@RequestBody注解

    具体请求内容和返回结果如下所示

image-20210515134858894

可以看到,报错了,提示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’格式的数据,如下:

post-json-@RequestBody-front-change-dateformat

但是这种办法对于前端很不友好(极其不好)

所以下面还是给出正常的解决办法

解决:所以这里的解决办法就是自己定义日期格式

  • 方案一:局部注解来解决,比如在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 {@Overridepublic 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")

总结

注解相关:

  1. @DateTimeFormat注解:适用于请求数据为非JSON数据,不会格式化返回数据
  2. @JsonFormat注解:适用于请求数据为JSON数据(尤其有日期数据时),且需在请求方法的参数前加@RequestBody`注解,会格式化返回数据
  3. @RequestBody注解:解析传来的JSON数据,转换成对应的Java对象
  4. @ResponseBody注解:转换Java对象为JSON数据,用来作为返回数据输出到前端

日期格式化相关:

  1. 请求非JSON数据,建议用@DateTimeFormat即可,此时不会格式化返回数据(比如get请求,当然get请求也可以请求JSON数据,只是不推荐)
  2. 请求JSON数据,建议用@ReqeustBody来转换数据,然后搭配局部注解@JsonFormat(会格式化返回数据)或者全局配置来修改默认的日期解析格式(默认"yyyy-MM-dd’T’HH:mm:ss.SSSX");全局配置也可以格式化返回数据,需配置builder.serializerByType
  3. 如果日期格式化出错,先看传来的数据是否为JSON数据(可以通过consumes来限制),然后再看有没有对于的注解或日期格式化全局配置

参考内容:

  • @RequestBody: https://blog.csdn.net/justry_deng/article/details/80972817/
  • @DateTimeFormat: https://segmentfault.com/a/1190000020423352

后记

学习之路漫漫,共勉之

写在最后:

愿你的意中人亦是中意你之人

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/556256.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

12 - java 类加载内存图

栈是线程私有的、堆是线程公有的如果不加 static 就是堆里面的属性,加了static 就是元数据区的属性

source code怎么用_code-builder代码生成器

背景本来code-builder是专门为MyBatis Enhance来编写的一块代码生成器code-builder可以用来做什么?code-builder是一款代码生成maven mojo插件,通过简单的配置就可以完成数据库内Table转换Entity或者其他实体类,想怎么生成完全根据你的个人业…

@Transactional注解的失效场景

Transactional注解的失效场景 引言 Transactional 注解相信大家并不陌生,平时开发中很常用的一个注解,它能保证方法内多个数据库操作要么同时成功、要么同时失败。使用Transactional注解时需要注意许多的细节,不然你会发现Transactional总是…

11 - java构造方法

java构造方法 构造方法 构造方法名与类名一致没有返回值使用 new 其实是在调用构造方法如果一个类里面没有构造方法,会自动创建一个空的构造方法构造方法能传参数,在构造期间就把对象的值赋好一旦有了新的有参数的构造方法,空的构造函数就不…

python无限循环的关键字_零基础学python-12.2 关键字pass,else,break,continue

关键字pass,else,break,continuepass:不做任何事情,只占位else:当正常跳出循环时执行break:满足某些条件跳出循环,不再执行任何东西continue:满足某些条件跳出本次循环,然后继续执行循环里面的东…

python中迭代器有哪些_Python迭代器:什么是Python中的迭代器以及如何使用它?

Python编程语言已经扩展了创新的每一个方面,包括机器学习、数据科学、人工智能等,这些概念是Python作为编程语言取得成功的基石。在本文中,我们将通过以下概念来理解PythoPython编程语言已经扩展了创新的每一个方面,包括机器学习、…

Spring 事务方法与非事务方法相互调用 @Transactional 注解失效不回滚?

写这篇文章的初衷呢就是最近遇到了一个Spring事务的大坑。与其说是坑,还不如说是自己事务这块儿太薄弱导致的(自嘲下)。 项目环境 Spring Boot 下面开始问题描述,发生的过程有点长,想直接看方案的直接跳过哦~&#x…

10 - java 权限修饰符

java权限修饰符 作用域当前类同package子孙类其他packagepublic√√√√protected√√√friendly(defult)√√private√ 为什么要时候用权限修饰符? 对自己而言,保护代码不受污染对别人而言,给别人一个干净的类

vue后端必须接口吗_前后端分离模式,后端说开发完才能给接口文档,合理吗

背景:汇总了下老王在其他平台的原创回复,欢迎关注老王原创公众号【软件老王】,关注不迷路。一、后端开发完接口才给出接口文档,合理吗?本人所在的项目组做项目过程中,后端不会先给出接口文档,而…

JAVA那点破事,并发、IO模型、集合、线程池、死锁、非阻塞、AQS...

JDK、JRE、JVM 三者有什么关系? 答案: JDK(全称 Java Development Kit),Java开发工具包,能独立创建、编译、运行程序。 JDK JRE java开发工具(javac.exe/java.exe/jar.exe) JRE(…

09 - java 包命名规范

java包命名规范 – 域名倒置 Java的包名都有小写单词组成,类名首字母大写;包的路径符合所开发的 系统模块的 定义,比如生产对生产,物资对物资,基础类对基础类。以便看了包名就明白是哪个模块,从而直接到对…

python memoryview_memoryview的用法

本来第一次看《流畅的python》觉得这部分没用,就跳过去,后面又出现,回头看还是一知半解,查了诸多资料,好像有一点明白了,立下帖子。1、memoryviewmemoryview()函数返回给定参数的内存查看对象(memory view)…

07 - java 方法里面的 return

Java 方法里面的 return return 之函数返回值 public static int sum(a, b) {return a b; }return 之结束方法 Scanner sc new Scanner(System.in): int num sc.nextInt();if (num 5) {return;System.out.println("哈哈哈,我是不会执行的"); // 函数遇…

世外桃源六python_六年匠心 桃花源记6月1日全民狂欢

六年匠心独运,烂漫之约!深圳淘乐携手并肩云畅游戏倾情打造出的人民良知连击手游《桃花源记》昨天宣布告一段落为期一周的小彩蛋检测!小彩蛋检测打开至今,诸位少主呼叫队友飘缈世外桃源的情绪高涨,网络服务器因而一度出現了满员的隆重开幕。而…

PTA 最小生成树-kruskal

7-92 最小生成树-kruskal 分数 10 全屏浏览题目 作者 任唯 单位 河北农业大学 题目给出一个无向连通图,要求求出其最小生成树的权值。 温馨提示:本题请使用kruskal最小生成树算法。 输入格式: 输出格式: 输出一个整数表示最小生成树的各边的长度之和。…

06 - java 方法

Java 方法 一个方法可以重复使用很多次,减少不必要的冗余,以及重复很多次不必要的操作让整个程序看起来模块化 public static void main (String[] args) {Scanner sc new Scanner(System.in);int num sc.nextInt();switch (num) {case 1: int[] arr…

Spring系列:父子容器详解

又一次被面试官带到坑里面了。 面试官:springmvc用过么? 我:用过啊,经常用呢 面试官:springmvc中为什么需要用父子容器? 我:嗯。。。没听明白你说的什么。 面试官:就是controll…

05 - 基本排序

01-冒泡排序 public static int[] bubbleSort(int[] arr) {for (int i 0; i < arr.length - 1; i) {for (int j 0; j < arr.length - i; j) {if (arr[i] > arr[i 1]) {// 两两交换int temp arr[i];arr[i] arr[i 1];arr[i 1] temp;}}}return arr; }02-选择排序…

java实现单链表

链表是java数据结构中一种很基础很常见却也很重要的数据结构&#xff0c;JDK中许多内置jar包基于单链表实现&#xff0c;比如像我们熟悉的linkedList等&#xff0c;为什么要使用链表呢&#xff1f; 我们知道java中很多集合的底层是基于数组实现的&#xff0c;数组有一个很重要…

sql 两表数据合并_多表查询SQL语句

本篇文章中主要讲述以下内容&#xff1a;一、表的加法合并两张表的过程&#xff1a;然后运用sql语句&#xff1a;select 课程号,课程名称 from course union select 课程号,课程名称 from course1以上子句会把两个表中重复数据删除。要想不删除重复的行&#xff0c;则需要在上面…