Pain points

When developing with Spring MVC, we often encounter a time string of a certain format coming from the front end that cannot be received directly with a specific type parameter in the java8 feature java.time package. We also have deserialization problems with receiving arguments with a java.time wrapper type. We also have formatting problems with returning arguments with a time type in front. Today we’re going to take care of them once and for all.

advice

In fact, the most scientific advice is to use a timestamp to represent the time. This is the best, avoiding front-end browser compatibility issues as well as serialization/deserialization issues with other middleware. But using time might be more semantic. Both approaches have their merits, and it’s not impossible to stick with java8’s time library. We will address each of these issues in java.time.localDateTime.

Local annotation mode

There are a lot of articles on the web saying that this annotation is front-end to back-end, that is, front-end to back-end passing time parameter formatting, and that is correct! There is a small problem, however, that this approach only works when deserialization is not involved. This applies to the following scenarios:

    @GetMapping("/local")
    public Map<String, String> data(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime localDateTime) {
        Map<String, String> map = new HashMap<>(1);
        map.put("data", localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        return map;
    }

Copy the code

Not if you use it in the following scenario:


@Data
public class UserInfo {

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime birthday;
    private String name;
    private Integer age;
}


   @PostMapping("/user")
    public Object postData(@RequestBody UserInfo userInfo) {
        System.out.println("userInfo = " + userInfo);
        return userInfo;
    }
Copy the code

The reason is that the Post request parameters are in the body and need to be deserialized into an object. The default is the Jackson library for deserialization and does not trigger the @dateTimeFormat annotation mechanism. This is where Jackson’s @jsonFormat annotation is needed. We can change the entity class UserInfo to the following:

@Data
public class UserInfo {

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime birthday;
    private String name;
    private Integer age;
}
Copy the code

The above two annotations can coexist, but it is important to be clear about how they are used. There is one more small detail: the format must correspond to the time type. For example, YYYY-MM-DD corresponds to java.time.LocalDate. @jsonFormat can be replaced by @Jsondeserialize and @jsonserialize if you make it a little more personalized. But their using arguments need to be implemented by you as your corresponding time type type. If @jsonFormat, @Jsondeserialize, and @jsonSerialize exist at the same time, @jsonFormat takes higher priority.

Benefits of local treatment

The advantage of local processing lies in eight words: let a hundred flowers blossom and a hundred schools of thought contend. Can maintain diversity, personalized. But parts bring a new problem: no common standards, incompatibilities. And it’s not easy to maintain. So sometimes we can manage it globally based on business needs. Now we’ll show you how to configure globally.

Globalize the time format configuration

Globalizing is also configured based on @DateTimeFormat and @jsonFormat scenarios. For the @dateTimeFormat scenario we implement the interface provided by Spring:

DateTimeFormatter :

     // Time formatting
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.CHINA);

Copy the code

Type conversion interface:

 org.springframework.core.convert.converter.Converter<S,T>
Copy the code

Implementation:

    @Bean
    public Converter<String, LocalDateTime> localDateConverter(a) {
        return new Converter<String, LocalDateTime>() {
            @Override
            public LocalDateTime convert(String source) {
                returnLocalDateTime.parse(source, FORMATTER); }}; }Copy the code

Or format the interface:

 org.springframework.format.Formatter<T>
Copy the code

Implementation:

    @Bean
    public Formatter<LocalDateTime> localDateFormatter(a) {
        return new Formatter<LocalDateTime>() {
            @Override
            public LocalDateTime parse(String text, Locale locale) throws ParseException {
                return LocalDateTime.parse(text, FORMATTER);
            }

            @Override
            public String print(LocalDateTime object, Locale locale) {
                returnobject.format(FORMATTER); }}; }Copy the code

The implementation of both interfaces must be registered as Spring beans and can be configured as one or the other, where S stands for Source, which is the front-end time string. T stands for Target, which stands for the time Java type that you need to convert or format.

For time serialization and deserialization we do the following (based on default Jackson, using LocalDateTime as an example) :

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer(a) {

        return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder
                 // deserialize
                .deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(FORMATTER))
                 / / the serialization
                .serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(FORMATTER));
    }

Copy the code

Also, the jsonMapper custom builder needs to be registered as a Spring Bean.

Global Configuration Essentials

Some of the advantages and disadvantages of global configuration have been explained above, but I will be verbose here to avoid you step on the pit. Global configuration is the same as local configuration. Also use pattern. This requires us to be consistent across the board. We can implement more than one of these global configurations for other adaptations such as LocalDate and OffsetDateTime. If we are using other middleware that requires serialization/deserialization, such as Redis and RabbitMQ, we will also need to adapt.

With the local and global handling of time formats described above, the Spring MVC timing issues that have plagued you are no longer there. If you feel right, please forward it and tell other students, like it and pay attention to it.

Pay attention to the public number: code nongxiao Pangge, get more information