如何在Spring Boot应用中优雅的使用Date和LocalDateTime

如何在Spring Boot应用中优雅的使用Date和LocalDateTime

Java8已经发布很多年了,但是很多人在开发时仍然坚持使用着Date和SimpleDateFormat进行时间操作。SimpleDateFormat不是线程安全的,而Date处理时间很麻烦,所以Java8提供了LocalDateTime、LocalDate和LocalTime等全新的时间操作API。无论是Date还是LocalDate,在开发Spring Boot应用时经常需要在每个实体类的日期字段上加上@DateTimeFormat注解来接收前端传值与日期字段绑定,加上@JsonFormat注解来让返回前端的日期字段格式化成我们想要的时间格式。时间和日期类型在开发中使用的频率是非常高的,如果每个字段都加上这两个注解的话是非常繁琐的,有没有一种全局设置的处理方式呢?今天就来向大家介绍一下。

注:本文基于Springboot2.3.0版本。

根据不同的请求方式需要做不同的配置,下文中分为了JSON方式传参和GET请求及POST表单方式传参两种情况。

JSON方式传参

这种情况指的是类型POST,Content-Type 是application/json 方式的请求。对于这类请求,controller中需要加上@RequestBody注解来标注到我们用来接收请求参数的局部变量上,代码如下:

@SpringBootApplication
@RestController
public class SpringbootDateLearningApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootDateLearningApplication.class, args);
    }
    
     /**
     * DateTime格式化字符串
     */
    private static final String DEFAULT_DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";

    /**
     * Date格式化字符串
     */
    private static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd";

    /**
     * Time格式化字符串
     */
    private static final String DEFAULT_TIME_PATTERN = "HH:mm:ss";

    public static class DateEntity {
        private LocalDate date;

        private LocalDateTime dateTime;

        private Date originalDate;

        public LocalDate getDate() {
            return date;
        }

        public void setDate(LocalDate date) {
            this.date = date;
        }

        public LocalDateTime getDateTime() {
            return dateTime;
        }

        public void setDateTime(LocalDateTime dateTime) {
            this.dateTime = dateTime;
        }

        public Date getOriginalDate() {
            return originalDate;
        }

        public void setOriginalDate(Date originalDate) {
            this.originalDate = originalDate;
        }

    }

    @RequestMapping("/date")
    public DateEntity getDate(@RequestBody DateEntity dateEntity) {
        return dateEntity;
    }
}    
复制代码

假设默认的接收和返回值的格式都是yyyy-MM-dd HH:mm:ss,可以有以下几个方案。

配置application.yml 文件

在application.yml文件中配置上如下内容:

spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
复制代码

小结:

  • 支持Content-Type 是application/json的POST请求,请求参数字符串和返回的格式都是yyyy-MM-dd HH:mm:ss如果请求参数是其他格式,如yyyy-MM-dd字符串则报400 Bad Request异常。
  • 不支持LocalDate等Java8日期API。

增加Jackson配置

/**
  * Jackson序列化和反序列化转换器,用于转换Post请求体中的json以及将对象序列化为返回响应的json
  */
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
    return builder -> builder
            .serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATETIME_PATTERN)))
            .serializerByType(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_PATTERN)))
            .serializerByType(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_PATTERN)))
            .serializerByType(Date.class, new DateSerializer(false, new SimpleDateFormat(DEFAULT_DATETIME_PATTERN)))
            .deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATETIME_PATTERN)))
            .deserializerByType(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_PATTERN)))
            .deserializerByType(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_PATTERN)))
            .deserializerByType(Date.class, new DateDeserializers.DateDeserializer(DateDeserializers.DateDeserializer.instance, new SimpleDateFormat(DEFAULT_DATETIME_PATTERN), DEFAULT_DATETIME_PATTERN))
            ;
}
复制代码

小结:

  • 支持Content-Type 是application/json的POST请求,请求参数字符串和返回的格式都是yyyy-MM-dd HH:mm:ss如果请求参数是其他格式,如yyyy-MM-dd字符串则报400 Bad Request异常。
  • 支持LocalDate等Java8日期API。

PS:上面的方式是通过配置一个Jackson2ObjectMapperBuilderCustomizerBean完成的,除了这种,也可以通过自定义一个MappingJackson2HttpMessageConverter来实现。

@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    ObjectMapper objectMapper = new ObjectMapper();
    // 指定时区
    objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
    // 日期类型字符串处理
    objectMapper.setDateFormat(new SimpleDateFormat(DEFAULT_DATETIME_PATTERN));

    // Java8日期日期处理
    JavaTimeModule javaTimeModule = new JavaTimeModule();
    javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATETIME_PATTERN)));
    javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_PATTERN)));
    javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_PATTERN)));
    javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATETIME_PATTERN)));
    javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_PATTERN)));
    javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_PATTERN)));
    objectMapper.registerModule(javaTimeModule);

    converter.setObjectMapper(objectMapper);
    return converter;
}
复制代码

以上几种方式都可以实现JSON传参时的全局化配置,更推荐后两种代码中增加配置bean的方式,可以同时支持Date和LocalDate。

GET请求及POST表单方式传参

这种方式和上面的JSON方式,在Spring Boot处理的方式是完全不同的。上一种JSON方式传参是在HttpMessgeConverter中通过jackson的ObjectMapper将http请求体转换成我们写在controller中的参数对象的,而这种方式用的是Converter接口(spring-core中定义的用于将源类型(一般是String)转成目标类型的接口),两者是有本质区别的。

自定义参数转换器(Converter)

自定义一个参数转换器,实现上面提到的org.springframework.core.convert.converter.Converter接口,在配置类里配置上以下几个bean,示例如下:

@Bean
public Converter<String, Date> dateConverter() {
    return new Converter<>() {
        @Override
        public Date convert(String source) {
            SimpleDateFormat formatter = new SimpleDateFormat(DEFAULT_DATETIME_PATTERN);
            try {
                return formatter.parse(source);
            } catch (Exception e) {
                throw new RuntimeException(String.format("Error parsing %s to Date", source));
            }
        }
    };
}

@Bean
public Converter<String, LocalDate> localDateConverter() {
    return new Converter<>() {
        @Override
        public LocalDate convert(String source) {
            return LocalDate.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATE_PATTERN));
        }
    };
}

@Bean
public Converter<String, LocalDateTime> localDateTimeConverter() {
    return new Converter<>() {
        @Override
        public LocalDateTime convert(String source) {
            return LocalDateTime.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATETIME_PATTERN));
        }
    };
}
复制代码

同时把controller接口增加一些参数,可以发现在接口里单独用变量接收也是可以正常转换的。

@RequestMapping("/date")
public DateEntity getDate(
        LocalDate date,
        LocalDateTime dateTime,
        Date originalDate,
        DateEntity dateEntity) {
    System.out.printf("date=%s, da
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值