可以优雅地丢弃@DateTimeFormat和@JsonFormat吗?

YES

当然可以

想想看我们在项目中什么时候会用到这两个注解呢?

  • Get请求传入java.util.Datejava.time.xxx类型参数,需要配上@DateTimeFormat来解析
  • Post请求传入json时候,同样需要在上述两种时间属性上加@JsonFormat反序列化
  • 返回对象里有java.util.Date需加@JsonFormat,配置好时区,不然时间会少8小时

先看看平常我们是怎么使用这两个注解的

@DateTimeFormat

@GetMapping("test")
    public Stu testGet(Stu stu) {
        log.info("stu get: " + stu);
        return stu;
    }
/**
 * 学生
 * @author wangwenwen
 * @date 2021/10/19 12:54
 * @version v1.0.0
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Stu {

    // 姓名
    private String name;

    // 生日
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime birth;

    // 毕业时间
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date graduate;

}

这两个参数如果不加@DateTimeFormat注解的话,会报以下的错误

Resolved [org.springframework.validation.BindException:
org.springframework.validation.BeanPropertyBindingResult: 2 errors
...省略一些日志...
[Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDateTime' for property 'birth';
...省略一些日志...
[Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for
property 'graduate';

@JsonFormat

 @PostMapping("test")
    public Stu testPost(@RequestBody Stu stu) {
        log.info("stu post: " + stu);
        return stu;
    }
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Stu {

    // 姓名
    private String name;

    // 生日
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime birth;

    // 毕业时间
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    private Date graduate;

}

如果不加@JsonFormat注解,则会报序列化错误

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: 
...省略部分日志... Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) 

...省略部分日志... JSON parse error: Cannot deserialize value of type `java.util.Date` 
from String "2029-01-01 10:00:00": not a valid representation 
(error: Failed to parse Date value '2029-01-01 10:00:00': Cannot parse date "2029-01-01 10:00:00": 

所以有了这两个注解,就能很好解决项目开发中参数入参、出参的问题。

但是,很多地方使用了这些注解时候,就要想想有没有全局统一配置呢?

下面给出全局配置的代码

核心依赖
   <dependencies>
    
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
    
        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>
    
        
        <dependency>
            <groupId>cn.hutoolgroupId>
            <artifactId>hutool-allartifactId>
            <version>5.6.6version>
        dependency>
    
        <dependency>
            <groupId>commons-langgroupId>
            <artifactId>commons-langartifactId>
            <version>2.6version>
        dependency>
    dependencies>
定义一些常量和Converter
public class DateConfig {

    /**
     * 默认日期时间格式
     */
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    /**
     * 默认日期格式
     */
    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    /**
     * 默认时间格式
     */
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
    /**
     * 默认年月格式
     */
    public static final String DEFAULT_YEAR_MONTH_FORMAT = "yyyy-MM";

    static class LocalDateTimeConverter implements Converter<String, LocalDateTime> {

        @Override
        public LocalDateTime convert(String source) {
            if (StringUtils.isBlank(source)) {
                return null;
            }
            if (StringUtils.isNumeric(source)) {
                return Instant.ofEpochMilli(Long.parseLong(source)).atZone(ZoneId.systemDefault()).toLocalDateTime();
            } else {
                return LocalDateTime.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT));
            }
        }
    }

    static class LocalDateConverter implements Converter<String, LocalDate> {

        @Override
        public LocalDate convert(String source) {
            if (StringUtils.isBlank(source)) {
                return null;
            }
            if (StringUtils.isNumeric(source)) {
                return Instant.ofEpochMilli(Long.parseLong(source)).atZone(ZoneId.systemDefault()).toLocalDate();
            } else {
                return LocalDate.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT));
            }
        }
    }

    static class LocalTimeConverter implements Converter<String, LocalTime> {

        @Override
        public LocalTime convert(String source) {
            if (StringUtils.isBlank(source)) {
                return null;
            }
            return LocalTime.parse(source, DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT));
        }
    }

    static class YearMonthConverter implements Converter<String, YearMonth> {

        @Override
        public YearMonth convert(String source) {
            if (StringUtils.isBlank(source)) {
                return null;
            }
            return YearMonth.parse(source, DateTimeFormatter.ofPattern(DEFAULT_YEAR_MONTH_FORMAT));
        }
    }

    static class DateConverter implements Converter<String, Date> {

        @Override
        public Date convert(String source) {
            if (StringUtils.isBlank(source)) {
                return null;
            }
            if (StringUtils.isNumeric(source)) {
                return new Date(Long.parseLong(source));
            } else {
                return DateUtil.parse(source);
            }
        }
    }
}
配置入参解析
@Configuration
public class ServletContextConfig implements WebMvcConfigurer {

    // 解析时间入参
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new DateConfig.LocalDateConverter());
        registry.addConverter(new DateConfig.LocalDateTimeConverter());
        registry.addConverter(new DateConfig.YearMonthConverter());
        registry.addConverter(new DateConfig.LocalTimeConverter());
        registry.addConverter(new DateConfig.DateConverter());
    }
}

自定义序列化和反序列器
@Component
public class MyJackson2ObjectMapperBuilderCustomizer implements Jackson2ObjectMapperBuilderCustomizer {
    
    @Override
    public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
        JavaTimeModule javaTimeModule = new JavaTimeModule();

        // =========================================== 序列化器 =========================================================

        // LocalDateTime序列化器
        javaTimeModule.addSerializer(LocalDateTime.class,
                new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DateConfig.DEFAULT_DATE_TIME_FORMAT)));

        // LocalDateTime序列化器
        javaTimeModule.addSerializer(LocalDate.class,
                new LocalDateSerializer(DateTimeFormatter.ofPattern(DateConfig.DEFAULT_DATE_FORMAT)));

        // LocalTime序列化器
        javaTimeModule.addSerializer(LocalTime.class,
                new LocalTimeSerializer(DateTimeFormatter.ofPattern(DateConfig.DEFAULT_TIME_FORMAT)));

        // YearMonth序列化器
        javaTimeModule.addSerializer(YearMonth.class,
                new YearMonthSerializer(DateTimeFormatter.ofPattern(DateConfig.DEFAULT_YEAR_MONTH_FORMAT)));

        // Date序列化器
        javaTimeModule.addSerializer(Date.class, new JsonSerializer<Date>() {
            @Override
            public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                jsonGenerator.writeString(DateUtil.format(date, DateConfig.DEFAULT_DATE_TIME_FORMAT));
            }
        });


        // =========================================== 反序列化器 =======================================================

        // LocalDateTime反序列化器
        javaTimeModule.addDeserializer(LocalDateTime.class, new JsonDeserializer<LocalDateTime>() {
            @Override
            public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
                String text = p.getText();
                if (StringUtils.isEmpty(text)) {
                    return null;
                }
                if (StringUtils.isNumeric(text)) {
                    return Instant.ofEpochMilli(Long.parseLong(text)).atZone(ZoneId.systemDefault()).toLocalDateTime();
                } else {
                    return LocalDateTime.parse(text, DateTimeFormatter.ofPattern(DateConfig.DEFAULT_DATE_TIME_FORMAT));
                }
            }
        });

        // LocalDate反序列化器
        javaTimeModule.addDeserializer(LocalDate.class, new JsonDeserializer<LocalDate>() {
            @Override
            public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
                String text = p.getText();
                if (StringUtils.isBlank(text)) {
                    return null;
                }
                if (StringUtils.isNumeric(text)) {
                    return Instant.ofEpochMilli(Long.parseLong(text)).atZone(ZoneId.systemDefault()).toLocalDate();
                } else {
                    return LocalDate.parse(text, DateTimeFormatter.ofPattern(DateConfig.DEFAULT_DATE_FORMAT));
                }
            }
        });

        // LocalTime反序列化器
        javaTimeModule.addDeserializer(LocalTime.class,
                new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DateConfig.DEFAULT_TIME_FORMAT)));

        // YearMonth反序列化器
        javaTimeModule.addDeserializer(YearMonth.class,
                new YearMonthDeserializer(DateTimeFormatter.ofPattern(DateConfig.DEFAULT_YEAR_MONTH_FORMAT)));

        // Date反序列化器
        javaTimeModule.addDeserializer(Date.class, new JsonDeserializer<Date>() {
            @Override
            public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
                String text = p.getText();
                if (StringUtils.isBlank(text)) {
                    return null;
                }
                if (StringUtils.isNumeric(text)) {
                    return new Date(Long.parseLong(text));
                } else {
                    return DateUtil.parse(text);
                }
            }
        });

        // 添加进去
        jacksonObjectMapperBuilder.modules(javaTimeModule);
    }
}

现在可以愉快的开发啦 不用再担心日期时间不能被解析,也不用担心时区问题导致时间不准确的问题。

你可能感兴趣的:(Spring,java,spring,restful)