栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

YES

当然可以

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

  • Get请求传入java.util.Date、java.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;
    }
@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": 

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

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

下面给出全局配置的代码

核心依赖
   
    
        
        
            org.springframework.boot
            spring-boot-starter-web
        
    
        
        
            org.projectlombok
            lombok
            true
        
    
        
        
            cn.hutool
            hutool-all
            5.6.6
        
    
        
            commons-lang
            commons-lang
            2.6
        
    
定义一些常量和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 {

        @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 {

        @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 {

        @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 {

        @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 {

        @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() {
            @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() {
            @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() {
            @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() {
            @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);
    }
}

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/336975.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号