SpringBoot Java8日期全局日期格式转换器


一. 前期准备

⏹日期正则标记注解

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface DatePattern {

    String value();
}

⏹日期格式类

  • 使用内部类聚合了LocalDateLocalDateTime格式的日期
// 日期格式定数
public final class Jsr310DateFormatPart {

	public static class LocalDateFormat {

        @DatePattern("^\\d{4}$")
        public static final String YYYY = "yyyy";

        @DatePattern("^\\d{4}\\d{2}$")
        public static final String YYYYMM = "yyyyMM";

        @DatePattern("^\\d{4}/\\d{2}$")
        public static final String YYYYMM_SLASH = "yyyy/MM";

        @DatePattern("^\\d{4}\\d{1,2}\\d{1,2}$")
        public static final String YYYYMMDD = "yyyyMMdd";

        @DatePattern("^\\d{4}/\\d{2}/\\d{2}$")
        public static final String YYYYMMDD_SLASH = "yyyy/MM/dd";

        @DatePattern("[0-9]+\\u5e74[0-9]+\\u6708[0-9]+\\u65e5$")
        public static final String YYYYMMDD_JP = "yyyy年MM月dd日";
    }

    public static class LocalDateTimeFormat {

        @DatePattern("^\\d{4}\\d{1,2}\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}$")
        public static final String YYYYMMDD_HHMMSS = "yyyyMMdd HH:mm:ss";

        @DatePattern("^\\d{4}/\\d{2}/\\d{2} \\d{1,2}:\\d{1,2}:\\d{1,2}$")
        public static final String YYYYMMDD_HHMMSS_SLASH = "yyyy/MM/dd HH:mm:ss";
    }
}

二. 日期格式转换工具类

import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

public final class Jsr310DateUtil {

    // LocalDate日期格式List
    private static final List<String> LocalDateFormatList = new ArrayList<>();

    // LocalDateTime日期格式List
    private static final List<String> LocalDateTimeFormatList = new ArrayList<>();

    // class与所对应格式的Map
    private static final Map<Class<?>, Map<String, String>> clsFormatMap = new HashMap<>();

    // 使用静态代码块,可以保证只初始化一次
    static {

        // 类型的class
        List<Class<?>> dateFormatClassList = Arrays.asList(
                Jsr310DateFormatPart.LocalDateFormat.class
                , Jsr310DateFormatPart.LocalDateTimeFormat.class
        );

        // LocalDate和LocalDateTime格式与正则的映射
        final Map<String, String> LocalDatePatternMap = new LinkedHashMap<>();
        final Map<String, String> LocalDateTimePatternMap = new LinkedHashMap<>();

        for (Class<?> dateFormatClass : dateFormatClassList) {

            // 获取所有的属性
            Field[] fields = dateFormatClass.getFields();
            for (Field field : fields) {

                // 获取属性上的注解
                DatePattern annotation = field.getAnnotation(DatePattern.class);
                if (ObjectUtils.isEmpty(annotation)) {
                    continue;
                }

                //  强制让属性可以访问
                ReflectionUtils.makeAccessible(field);

                // 日期格式化字符串
                String dateFormatStr = "";
                try {
                    // 获取当前属性所对应的属性值
                    dateFormatStr = (String)field.get(dateFormatClass);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }

                // 根据类型将LocalDate和LocalDateTime的初始值List和Map获取好
                if (Jsr310DateFormatPart.LocalDateFormat.class.equals(dateFormatClass)) {
                    LocalDateFormatList.add(dateFormatStr);
                    LocalDatePatternMap.put(dateFormatStr, annotation.value());
                } else if (Jsr310DateFormatPart.LocalDateTimeFormat.class.equals(dateFormatClass)) {
                    LocalDateTimeFormatList.add(dateFormatStr);
                    LocalDateTimePatternMap.put(dateFormatStr, annotation.value());
                }
            }
        }

        clsFormatMap.putAll(
            new HashMap<>() {
                {
                    put(LocalDate.class, LocalDatePatternMap);
                    put(LocalDateTime.class, LocalDateTimePatternMap);
                }
            }
        );
    }

    // 字符串转换LocalDate
    public static LocalDate formatDateStrToLocalDateAllFormat(String dateStr) {

        return Jsr310DateUtil.formatDateStrToAllJsr310Date(dateStr, LocalDate.class);
    }

    // 字符串转LocalDateTime
    public static LocalDateTime formatDateStrToLocalDateTimeAllFormat(String dateStr) {

        return Jsr310DateUtil.formatDateStrToAllJsr310Date(dateStr, LocalDateTime.class);
    }

    @SuppressWarnings("unchecked")
    private static <T> T formatDateStrToAllJsr310Date(String dateStr, Class<T> cls) {

        if (ObjectUtils.isEmpty(dateStr)) {
            return null;
        }

        // 根据class的类型获取所对应的map
        Map<String, String> formatPatternMap = clsFormatMap.get(cls);
        for (Map.Entry<String, String> mapEntry : formatPatternMap.entrySet()) {

            if (!dateStr.matches(mapEntry.getValue())) {
                continue;
            }

            if (LocalDate.class.equals(cls)) {
                return (T) Jsr310DateUtil.formatDateStrToLocalDate(dateStr, mapEntry.getKey());
            } else if (LocalDateTime.class.equals(cls)) {
                return (T) Jsr310DateUtil.formatStringToLocalDateTime(dateStr, mapEntry.getKey());
            }
        }

        return null;
    }

    // 指定的格式转换为LocalDate
    public static LocalDate formatDateStrToLocalDate(String dateStr, String format) {

        if (ObjectUtils.isEmpty(dateStr) || !LocalDateFormatList.contains(format)) {
            return null;
        }

        return LocalDate.parse(dateStr, DateTimeFormatter.ofPattern(format));
    }

    // 指定的格式转换为LocalDate
    public static LocalDateTime formatStringToLocalDateTime(String dateStr, String format) {

        if (ObjectUtils.isEmpty(dateStr) || !LocalDateTimeFormatList.contains(format)) {
            return null;
        }

        return LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern(format));
    }
}

三. 创建全局Jsr310日期序列化与反序列化转换类

  • 我们通过内部类继承JsonSerializer<T>JsonDeserializer<T>来实现日期的序列与反序列化
  • 通过builderJsr310JavaTimeModule()方法来创建聚合好的模块
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public final class GlobalJsr310DateConvert {

    // 将LocalDate日期序列化为日期字符串
    private static class CustomLocalDateSerializer extends JsonSerializer<LocalDate> {
        // LocalDate ==> yyyy年MM月dd日
        @Override
        public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeString(value.format(DateTimeFormatter.ofPattern(Jsr310DateFormatPart.LocalDateFormat.YYYYMMDD_JP)));
        }
    }

    // 将日期字符串反序列化为LocalDate日期
    private static class CustomLocalDateDeserializer extends JsonDeserializer<LocalDate> {
        @Override
        public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
            String dateStr = p.getValueAsString();
            return Jsr310DateUtil.formatDateStrToLocalDateAllFormat(dateStr);
        }
    }

    // 将LocalDateTime日期序列化为日期字符串
    private static class CustomLocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
        // LocalDateTime ==> yyyy/MM/dd HH:mm:ss
        @Override
        public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeString(value.format(DateTimeFormatter.ofPattern(Jsr310DateFormatPart.LocalDateTimeFormat.YYYYMMDD_HHMMSS_SLASH)));
        }
    }

    // 将日期字符串反序列化为LocalDateTime日期
    private static class CustomLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
        @Override
        public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
            String dateStr = p.getValueAsString();
            return Jsr310DateUtil.formatDateStrToLocalDateTimeAllFormat(dateStr);
        }
    }
	
	// 创建日期序列化与反序列化的模块
    public static JavaTimeModule builderJsr310JavaTimeModule() {

        JavaTimeModule javaTimeModule = new JavaTimeModule();

        // LocalDate的序列和反序列化
        javaTimeModule.addSerializer(LocalDate.class, new CustomLocalDateSerializer());
        javaTimeModule.addDeserializer(LocalDate.class, new CustomLocalDateDeserializer());

        // LocalDateTime的序列和反序列化
        javaTimeModule.addSerializer(LocalDateTime.class, new CustomLocalDateTimeSerializer());
        javaTimeModule.addDeserializer(LocalDateTime.class, new CustomLocalDateTimeDeserializer());

        return javaTimeModule;
    }
}

四. 将转换类注册到ObjectMapper中

  • 因为ObjectMapper中已经有一部分的时间相关的模块被注册了,所以我们需要先通过MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS来忽略重复模块的注册
import com.fasterxml.jackson.databind.*;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

@Configuration
public class CustomConfig {

    @Bean
    @Primary
    @ConditionalOnMissingBean
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {

        // 通过Jackson2ObjectMapperBuilder的构建者模式创建ObjectMapper对象
        return builder
                // 禁用忽略重复的模块注册
                .featuresToDisable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS)
                // 使用我们自定义的jsr310时间模块
                .modules(GlobalJsr310DateConvert.builderJsr310JavaTimeModule())
                // 将忽略重复的模块注册重新生效,避免被其他地方覆盖
                .featuresToDisable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS)
                .build();
    }
}

五. 效果

⏹后台Form

import lombok.Data;

import java.time.LocalDate;
import java.time.LocalDateTime;

@Data
public class Test15Form {

    private String name;

    private String hobby;

    private String address;
	
	// java8日期
    private LocalDateTime loginTime;
	// java8日期
    private LocalDate birthday;
}

⏹前台JS

const jsonData = {
    name: '贾飞天',
    hobby: '睡觉',
    // 对应后台form的LocalDate格式的数据
    birthday: '20210105',
    // 对应后台form的LocalDateTime格式的数据
    loginTime: '20210915 05:15:10'
};

$.ajax({
    url: url,
    type: 'POST',
    // 对象转换为json字符串
    data: JSON.stringify(jsonData),
    contentType: "application/json",
    success: function (data, status, xhr) {
        console.log(data);
    }
});

💪反序列化的效果

在这里插入图片描述

💪序列化的效果

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值