【SpringBoot】整合Jackson日期序列化

一、引言

Jackson作为Springboot的默认JSON库,对于JDK8的日期序列化没有做默认的支持,需要手动加入jsr310的依赖。
同时,优雅的API设计也应该统一处理日期的序列化,否则前端就需要各种判断。
按照阿里Java开发手册中推荐:前后端的时间格式统一为"yyyy-MM-dd HH:mm:ss",统一为 GMT。
本文详细讲述基于Jackson的日期序列化(没有海外业务,默认GMT+8),可通过properties修改。

二、配置

1. 增加POM依赖

<!--JAVA 8 日期格式化 -->
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

2. 编写配置类

DateConverterProperties ,设置默认格式,可按需通过yml覆盖

package com.qbhj.casejacksonserializer.properties;

import java.util.Locale;
import java.util.TimeZone;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * 日期序列化配置类
 * @author gmlee
 * @date 2022/5/16
 */
@Data
@ConfigurationProperties(prefix = "jackson")
public class DateConverterProperties {
    
    

    /**
     * Date/LocalDateTime 格式化
     */
    private String dateTimeFormat = "yyyy-MM-dd HH:mm:ss";

    /**
     * localDate 格式化
     */
    private String localDateFormat = "yyyy-MM-dd";

    /**
     * localTime 格式化
     */
    private String localTimeFormat = "HH:mm:ss";

    /**
     * 时区, 默认 GMT+8
     */
    private TimeZone timeZone = TimeZone.getTimeZone("GMT+8");

    /**
     * 地区, 默认 zh_CN
     */
    private Locale locale = Locale.CHINA;

}

MyDateConverterConfig配置类,指定JDK8日期类的序列化和反序列化器

package com.qbhj.casejacksonserializer.config;

import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.deser.std.DateDeserializers;
import com.fasterxml.jackson.databind.ser.std.DateSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.qbhj.casejacksonserializer.properties.DateConverterProperties;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;

/**
 * 基于Jackson的日期格式配置
 *
 * @author gmlee
 * @date 2022/5/16
 */
@Slf4j
@Configuration
@EnableConfigurationProperties(DateConverterProperties.class)
public class MyDateConverterConfig {
    
    

    @Autowired
    private DateConverterProperties props;

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
    
    
        log.info("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
        log.info("@================= 初始化jackson日期序列化 ==================@");
        log.info("@==========================================================@");
        log.info("@========== Locale : 【{}】 ============================@", props.getLocale());
        log.info("@========== TimeZone : 【{}】 ======================@", props.getTimeZone().getDisplayName());
        log.info("@========== Date : 【{}】 =================@", props.getDateTimeFormat());
        log.info("@========== LocalDateTime : 【{}】 ========@", props.getDateTimeFormat());
        log.info("@========== LocalDate : 【{}】 =====================@", props.getLocalDateFormat());
        log.info("@========== LocalTime : 【{}】 =======================@", props.getLocalTimeFormat());
        log.info("@==========================================================@");
        log.info("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");

        final DateTimeFormatter dateTimeFormatter = this.ofPattern(props.getDateTimeFormat());
        final DateTimeFormatter localDateFormatter = this.ofPattern(props.getLocalDateFormat());
        final DateTimeFormatter localTimeFormatter = this.ofPattern(props.getLocalTimeFormat());
        final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(props.getDateTimeFormat());

        return builder -> {
    
    
            builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                // 地区
                .locale(props.getLocale())
                // 时区
                .timeZone(props.getTimeZone());
            // 序列化
            builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter))
                .serializerByType(LocalDate.class, new LocalDateSerializer(localDateFormatter))
                .serializerByType(LocalTime.class, new LocalTimeSerializer(localTimeFormatter))
                .serializerByType(Date.class, new DateSerializer(false, simpleDateFormat));
            // 反序列化
            builder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter))
                .deserializerByType(LocalDate.class, new LocalDateDeserializer(localDateFormatter))
                .deserializerByType(LocalTime.class, new LocalTimeDeserializer(localTimeFormatter))
                .deserializerByType(Date.class,
                    new DateDeserializers.DateDeserializer(DateDeserializers.DateDeserializer.instance,
                        simpleDateFormat, props.getDateTimeFormat()));
        };
    }


    /**
     * 根据字符串 获取 DateTimeFormatter
     *
     * @param pattern 日期格式字符串
     * @return DateTimeFormatter
     */
    private DateTimeFormatter ofPattern(String pattern) {
    
    
        return DateTimeFormatter.ofPattern(pattern);
    }


    /**
     * LocalDate转换器,用于转换RequestParam和PathVariable参数
     */
    @Bean
    public Converter<String, LocalDate> localDateConverter() {
    
    
        return new Converter<String, LocalDate>() {
    
    
            @Override
            public LocalDate convert(String source) {
    
    
                return LocalDate.parse(source, DateTimeFormatter.ofPattern(props.getLocalDateFormat()));
            }
        };
    }

    /**
     * LocalDateTime转换器,用于转换RequestParam和PathVariable参数
     */
    @Bean
    public Converter<String, LocalDateTime> localDateTimeConverter() {
    
    
        return new Converter<String, LocalDateTime>() {
    
    
            @Override
            public LocalDateTime convert(String source) {
    
    
                return LocalDateTime.parse(source, DateTimeFormatter.ofPattern(props.getDateTimeFormat()));
            }
        };
    }

    /**
     * LocalTime转换器,用于转换RequestParam和PathVariable参数
     */
    @Bean
    public Converter<String, LocalTime> localTimeConverter() {
    
    
        return new Converter<String, LocalTime>() {
    
    
            @Override
            public LocalTime convert(String source) {
    
    
                return LocalTime.parse(source, DateTimeFormatter.ofPattern(props.getLocalTimeFormat()));
            }
        };
    }

    /**
     * Date转换器,用于转换RequestParam和PathVariable参数
     */
    @Bean
    public Converter<String, Date> dateConverter() {
    
    
        return new Converter<String, Date>() {
    
    
            @Override
            public Date convert(String source) {
    
    
                final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(props.getDateTimeFormat());

                try {
    
    
                    return simpleDateFormat.parse(source);
                } catch (ParseException e) {
    
    
                    log.error(">>>> init Converter String to Date error!", e);
                    throw new RuntimeException(e);
                }
            }
        };
    }

}

3. 编写测试接口

DateSerializerTestEntity 日期类型Entity

package com.qbhj.casejacksonserializer.entity;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
import lombok.Data;

/**
 * 日期序列化测试实体类
 *
 * @author gmlee
 * @date 2021/5/16
 */
@Data
public class DateSerializerTestEntity {
    
    

    private LocalDateTime localDateTime;

    private LocalDate localDate;

    private LocalTime localTime;

    private Date date;
}

DateSerializerController测试接口

package com.qbhj.casejacksonserializer.controller;

import com.qbhj.casejacksonserializer.entity.DateSerializerTestEntity;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * 日期序列化测试控制器
 *
 * @author gmlee
 * @date 2022/5/16
 */
@RequestMapping("/date")
@RestController
public class DateSerializerController {
    
    

    /**
     * 获取日期-json序列化
     */
    @GetMapping("/get")
    public DateSerializerTestEntity get() {
    
    
        DateSerializerTestEntity dateEntity = new DateSerializerTestEntity();
        dateEntity.setLocalDateTime(LocalDateTime.now());
        dateEntity.setLocalDate(LocalDate.now());
        dateEntity.setLocalTime(LocalTime.now());
        dateEntity.setDate(new Date());

        return dateEntity;
    }

    /**
     * json反序列化
     */
    @PostMapping("/set_body")
    public DateSerializerTestEntity setBody(@RequestBody DateSerializerTestEntity dateEntity) {
    
    
        return dateEntity;
    }

    /**
     * 参数解析
     *
     */
    @GetMapping("/get_params")
    public DateSerializerTestEntity getParams(DateSerializerTestEntity dateEntity) {
    
    
        return dateEntity;
    }

    /**
     * RequestParam 反序列化测试
     */
    @PostMapping("/set_params")
    public DateSerializerTestEntity setParams(@RequestParam Date date, @RequestParam LocalDateTime localDateTime,
        @RequestParam LocalDate localDate, @RequestParam LocalTime localTime) {
    
    
        DateSerializerTestEntity entity = new DateSerializerTestEntity();
        entity.setDate(date);
        entity.setLocalDateTime(localDateTime);
        entity.setLocalDate(localDate);
        entity.setLocalTime(localTime);
        return entity;
    }

    /**
     * PathVariable参数解析
     */
    @GetMapping("/get/{date}/{localDateTime}/{localDate}/{localTime}")
    public DateSerializerTestEntity getPath(@PathVariable Date date, @PathVariable LocalDateTime localDateTime,
        @PathVariable LocalDate localDate, @PathVariable LocalTime localTime) {
    
    
        DateSerializerTestEntity entity = new DateSerializerTestEntity();
        entity.setDate(date);
        entity.setLocalDateTime(localDateTime);
        entity.setLocalDate(localDate);
        entity.setLocalTime(localTime);
        return entity;
    }

}

三、测试

1. 启动日志

在这里插入图片描述

2. API测试,日期序列化格式 yyyy-MM-dd HH:mm:ss

2.1 序列化

在这里插入图片描述

2.2 params反序列化

在这里插入图片描述

2.3 @RequestBody反序列化

在这里插入图片描述

2.4 @RequestParam反序列化

在这里插入图片描述

2.5 @PathVariable反序列化

在这里插入图片描述

四、代码

https://gitee.com/qbhj/java-cases/tree/master/case-jackson-serializer

猜你喜欢

转载自blog.csdn.net/weixin_43582081/article/details/124810659