时间的格式有很多种,比较常用的有时间戳格式、UTC时间格式、标准时间格式等,而且时间参数出现的位置可能在URL上,可能在Body中,也可能在Header中,我们创建一个简单的接口,想通过@PathVariable接收Date类型的时间参数,和通过@RequestParam接收LocalDateTime类型的时间参数,并且想通过@RequestBody来接收JSON中的时间参数:

如何优雅的接收时间类型参数?_ide

jsGET http://localhost:80/time/2023-09-10?time=2023-09-15 11:11:11
Accept: application/json
Content-Type: application/json

{
    "id":1,
    "now":"2023-09-15 13:50:10",
    "day":"2023-09-15",
    "time": "2023-09-15 13:50:10",
    "timeStack": 1694757010407
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

SpringBoot默认是用String接收的,如果直接用LocalDateTime或者Date来接收会报一个类型转换错误。

当然我们可以使用String接收,再手动转成对应的时间格式,但是不够优雅,接下来我们看看不同级别是如何处理时间字段的。

青铜解决方案

如何优雅的接收时间类型参数?_java_02

如何优雅的接收时间类型参数?_解决方案_03

青铜解析方案存在的问题:

  1. @InitBinder作用域只是当前的Controller,如果我用100个Controller难道我要写100个@InitBinder
  2. @JsonFormat 也是每个字段上都要增加个注解,而且只能支持一种时间格式,如果我们还要支持时间戳格式就没法做到了。

白银解决方案

针对青铜解析方案存在的问题1,我们的解决方案是使用@ControllerAdvice,这样就不用在每个Controller上都添加@InitBinder

java@ControllerAdvice
public class GlobalControllerAdvice {

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(LocalDateTime.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) {
                setValue(DateUtil.parseLocalDateTime(text));
            }
        });
        binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) {
                setValue(DateUtil.parse(text,NORM_DATETIME_PATTERN,NORM_DATE_PATTERN));
            }
        });
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

如何优雅的接收时间类型参数?_解决方案_04


java@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Bean
    @Primary
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
        module.addSerializer(Date.class, new DateTimeSerializer());
        module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());
        module.addDeserializer(Date.class, new DateTimeDeserializer());
        mapper.registerModule(module);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        return JsonUtils.getMapper();
    }
}

public class DateTimeDeserializer extends StdDeserializer<Date> {

    public DateTimeDeserializer() {
        this(null);
    }

    public DateTimeDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public Date deserialize(JsonParser jp, DeserializationContext ctx)
            throws IOException {
        String value = jp.getValueAsString();
            return DateUtil.parse(value,NORM_DATETIME_PATTERN,NORM_DATE_PATTERN);
    }
}

public class DateTimeSerializer extends StdSerializer<Date> {

    public DateTimeSerializer() {
        this(null);
    }

    public DateTimeSerializer(Class<Date> t) {
        super(t);
    }

    @Override
    public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
        jgen.writeString(DateUtil.format(value, DatePattern.NORM_DATETIME_PATTERN));
    }
}

public class LocalDateTimeDeserializer extends StdDeserializer<LocalDateTime> {

    public LocalDateTimeDeserializer() {
        this(null);
    }

    public LocalDateTimeDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public LocalDateTime deserialize(JsonParser jp, DeserializationContext ctx)
            throws IOException {
        String value = jp.getValueAsString();
        if (StrUtil.isNumeric(value)) {
            Date date = new Date(jp.getLongValue());
            return LocalDateTime.ofInstant(date.toInstant(),  ZoneId.of("Asia/Shanghai"));
        } else {
            return DateUtil.parseLocalDateTime(value);
        }
    }
}

public class LocalDateTimeSerializer extends StdSerializer<LocalDateTime> {

    public LocalDateTimeSerializer() {
        this(null);
    }

    public LocalDateTimeSerializer(Class<LocalDateTime> t) {
        super(t);
    }

    @Override
    public void serialize(LocalDateTime value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
        jgen.writeString(LocalDateTimeUtil.formatNormal(value));
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.

如何优雅的接收时间类型参数?_ide_05

王者解决方案


如何优雅的接收时间类型参数?_java_06


java@Configuration
public class WebConfig  {
    @Bean
    @Primary
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
        module.addSerializer(Date.class, new DateTimeSerializer());
        module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());
        module.addDeserializer(Date.class, new DateTimeDeserializer());
        mapper.registerModule(module);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        return JsonUtils.getMapper();
    }

    @Bean
    public Converter<String, LocalDateTime> stringLocalDateTimeConverter() {
        return new Converter<String, LocalDateTime>() {
            @Override
            public LocalDateTime convert(String source) {
                if (StrUtil.isNumeric(source)) {
                    return LocalDateTimeUtil.of(Long.parseLong(source));
                } else {
                    return DateUtil.parseLocalDateTime(source);
                }
            }
        };

    }

    @Bean
    public Converter<String, Date> stringDateTimeConverter() {
        return new Converter<String, Date>() {
            @Override
            public Date convert(String source) {
                if (StrUtil.isNumeric(source)) {
                    return new Date(Long.parseLong(source));
                } else {
                    return DateUtil.parse(source);
                }
            }
        };
    }

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.

最后说一句(求关注!别白嫖!)

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。

关注公众号:woniuxgg,在公众号中回复:笔记  就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!