关于java:日期格式化时注解DateTimeFormat无效的问题分析

11次阅读

共计 5937 个字符,预计需要花费 15 分钟才能阅读完成。

作者:汤圆

集体博客: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”)

总结

注解相干:

  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_…
  • @DateTimeFormat: https://segmentfault.com/a/11…

后记

学习之路漫漫,共勉之

写在最初:

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

正文完
 0