SpringBoot对接收及返回Instant类型的处理

一:处理post请求json中的Instant类型
1.springboot中日期格式化配置:

spring:
    jackson:
        date-format: yyyy-MM-dd HH:mm:ss
        time-zone: GMT+8

以上配置只对Date类型的日期格式化有效,对LocalDateTime ,Instant无效,需要添加以下配置:

package com.mycompany.myapp.config;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.zalando.problem.ProblemModule;
import org.zalando.problem.violations.ConstraintViolationProblemModule;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.DateSerializer;
import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.module.afterburner.AfterburnerModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import com.mycompany.myapp.utils.DateFormatUtil;

@Configuration
public class JacksonConfiguration {

    @Value("${spring.jackson.date-format}")
    private String formatValue;

    @Bean(name = "format")
    public DateTimeFormatter format() {
        return DateTimeFormatter.ofPattern(formatValue);
    }

    @Bean
    public ObjectMapper serializingObjectMapper(@Qualifier("format") DateTimeFormatter format) {
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(format));
        javaTimeModule.addSerializer(Instant.class, new InstantCustomSerializer(format));
        javaTimeModule.addSerializer(Date.class, new DateSerializer(false, new SimpleDateFormat(formatValue)));
        javaTimeModule.addDeserializer(Instant.class, new InstantCustomDeserializer());
        javaTimeModule.addDeserializer(Date.class, new DateCustomDeserializer());
        ObjectMapper mapper = new ObjectMapper()
                .registerModule(new ParameterNamesModule())
                .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .registerModule(javaTimeModule);
        return mapper;
    }

    class InstantCustomSerializer extends JsonSerializer {
        private DateTimeFormatter format;

        private InstantCustomSerializer(DateTimeFormatter formatter) {
            this.format = formatter;
        }

        @Override
        public void serialize(Instant instant, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            if (instant == null) {
                return;
            }
            String jsonValue = format.format(instant.atZone(ZoneId.systemDefault()));
            jsonGenerator.writeString(jsonValue);
        }
    }

    class InstantCustomDeserializer extends JsonDeserializer{

        @Override
        public Instant deserialize(JsonParser p, DeserializationContext ctxt)
                throws IOException, JsonProcessingException {
            String dateString = p.getText().trim();
            if(StringUtils.isNotBlank(dateString)){
                Date pareDate;
                try {
                    pareDate = DateFormatUtil.pareDate(dateString);
                    if(null != pareDate){
                        return pareDate.toInstant();
                    }
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }

    }

    class DateCustomDeserializer extends JsonDeserializer{

        @Override
        public Date deserialize(JsonParser p, DeserializationContext ctxt)
                throws IOException, JsonProcessingException {
            String dateString = p.getText().trim();
            if(StringUtils.isNotBlank(dateString)){
                try {
                    return DateFormatUtil.pareDate(dateString);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }

    }

    /**
     * Support for Java date and time API.
     * @return the corresponding Jackson module.
     */
    @Bean
    public JavaTimeModule javaTimeModule() {
        return new JavaTimeModule();
    }

    @Bean
    public Jdk8Module jdk8TimeModule() {
        return new Jdk8Module();
    }


    /*
     * Support for Hibernate types in Jackson.
     */
    @Bean
    public Hibernate5Module hibernate5Module() {
        return new Hibernate5Module();
    }

    /*
     * Jackson Afterburner module to speed up serialization/deserialization.
     */
    @Bean
    public AfterburnerModule afterburnerModule() {
        return new AfterburnerModule();
    }

    /*
     * Module for serialization/deserialization of RFC7807 Problem.
     */
    @Bean
    ProblemModule problemModule() {
        return new ProblemModule();
    }

    /*
     * Module for serialization/deserialization of ConstraintViolationProblem.
     */
    @Bean
    ConstraintViolationProblemModule constraintViolationProblemModule() {
        return new ConstraintViolationProblemModule();
    }

}
package com.mycompany.myapp.utils;

import java.text.ParseException;
import java.time.Instant;
import java.util.Date;

import javax.validation.constraints.NotNull;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.FastDateFormat;

/**
* @author xuzhipeng
* @date 2018-11-22 13:51:05
* @since 1.0
*/
public class DateFormatUtil {

    public static final String SYMBOL_DOT = "\\.";

    public static final String DATE_REGEX_YYYYMM = "^\\d{4}-\\d{1,2}$";//日期正则yyyy-MM
    public static final String DATE_REGEX_YYYYMMDD = "^\\d{4}-\\d{1,2}-\\d{1,2}$";//日期正则yyyy-MM-dd
    public static final String DATE_REGEX_YYYYMMDDHHMM = "^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}$";//日期正则yyyy-MM-dd hh:mm
    public static final String DATE_REGEX_YYYYMMDDHHMMSS = "^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$";//日期正则yyyy-MM-dd hh:mm:ss
    public static final String DATE_REGEX_SECOND_DOT_NANOSECOND = "^[0-9]{1,}\\.[0-9]{1,9}$";//Instant日期秒+纳秒
    public static final String DATE_REGEX_YYYYMMDD_T_HHMMSS_Z = "^\\d{4}-\\d{1,2}-\\d{1,2}T\\d{1,2}:\\d{1,2}:\\d{1,2}Z$";//日期正则yyyy-MM-dd'T'HH:mm:ssZ
    public static final String DATE_REGEX_YYYYMMDD_T_HHMMSS_SSS_Z = "^\\d{4}-\\d{1,2}-\\d{1,2}T\\d{1,2}:\\d{1,2}:\\d{1,2}\\.\\d{1,3}Z$";//日期正则yyyy-MM-dd'T'HH:mm:ss.SSSZ


    // 以T分隔日期和时间,并带时区信息,符合ISO8601规范
    public static final String PATTERN_ISO = "yyyy-MM-dd'T'HH:mm:ss.SSSZZ";
    public static final String PATTERN_ISO_ON_SECOND = "yyyy-MM-dd'T'HH:mm:ssZZ";
    public static final String PATTERN_ISO_ON_DATE = "yyyy-MM-dd";
    public static final String PATTERN_ISO_ON_MONTH = "yyyy-MM";

    // 以空格分隔日期和时间,不带时区信息
    public static final String PATTERN_DEFAULT = "yyyy-MM-dd HH:mm:ss.SSS";
    public static final String PATTERN_DEFAULT_ON_SECOND = "yyyy-MM-dd HH:mm:ss";
    public static final String PATTERN_DEFAULT_ON_MINUTE = "yyyy-MM-dd HH:mm";

    // 使用工厂方法FastDateFormat.getInstance(), 从缓存中获取实例

    // 以T分隔日期和时间,并带时区信息,符合ISO8601规范
    public static final FastDateFormat ISO_FORMAT = FastDateFormat.getInstance(PATTERN_ISO);
    public static final FastDateFormat ISO_ON_SECOND_FORMAT = FastDateFormat.getInstance(PATTERN_ISO_ON_SECOND);
    public static final FastDateFormat ISO_ON_DATE_FORMAT = FastDateFormat.getInstance(PATTERN_ISO_ON_DATE);
    public static final FastDateFormat ISO_ON_MONTH_FORMAT = FastDateFormat.getInstance(PATTERN_ISO_ON_MONTH);

    // 以空格分隔日期和时间,不带时区信息
    public static final FastDateFormat DEFAULT_FORMAT = FastDateFormat.getInstance(PATTERN_DEFAULT);
    public static final FastDateFormat DEFAULT_ON_SECOND_FORMAT = FastDateFormat.getInstance(PATTERN_DEFAULT_ON_SECOND);
    public static final FastDateFormat DEFAULT_ON_MINUTE_FORMAT = FastDateFormat.getInstance(PATTERN_DEFAULT_ON_MINUTE);

    /**
     * 将日期格式的字符串转换成指定格式的日期
     * @param pattern 日期格式
     * @param dateString 日期字符串
     * @return
     * @throws ParseException
     */
    public static Date pareDate(@NotNull String pattern, @NotNull String dateString) throws ParseException {
        return FastDateFormat.getInstance(pattern).parse(dateString);
    }

    /**
     * 将日期格式的字符串根据正则转换成相应格式的日期
     * @param dateString 日期字符串
     * @return
     * @throws ParseException
     */
    public static Date pareDate(@NotNull String dateString) throws ParseException {
        String source = dateString.trim();
        if (StringUtils.isNotBlank(source)) {
            if(source.matches(DATE_REGEX_YYYYMM)){
                return ISO_ON_MONTH_FORMAT.parse(source);
            }else if(source.matches(DATE_REGEX_YYYYMMDD)){
                return ISO_ON_DATE_FORMAT.parse(source);
            }else if(source.matches(DATE_REGEX_YYYYMMDDHHMM)){
                return DEFAULT_ON_MINUTE_FORMAT.parse(source);
            }else if(source.matches(DATE_REGEX_YYYYMMDDHHMMSS)){
                return DEFAULT_ON_SECOND_FORMAT.parse(source);
            }else if(source.matches(DATE_REGEX_YYYYMMDD_T_HHMMSS_Z)){
                return ISO_ON_SECOND_FORMAT.parse(source);
            }else if(source.matches(DATE_REGEX_YYYYMMDD_T_HHMMSS_SSS_Z)){
                return ISO_FORMAT.parse(source);
            }else if(source.matches(DATE_REGEX_SECOND_DOT_NANOSECOND)){
                String[] split = source.split(SYMBOL_DOT);
                return Date.from(Instant.ofEpochSecond(Long.parseLong(split[0]), Long.parseLong(split[1])));
            }else {
                throw new IllegalArgumentException("Invalid date value '" + source + "'");
            }
        }
        return null;
    }

}

并在pom文件中添加以下依赖:


    com.fasterxml.jackson.module
    jackson-module-parameter-names


    com.fasterxml.jackson.datatype
    jackson-datatype-jdk8


    com.fasterxml.jackson.datatype
    jackson-datatype-jsr310


     org.apache.commons
     commons-lang3

参考:
1.http://www.cnblogs.com/liwc/p/9621435.html;
2.https://my.oschina.net/MeiJianMing/blog/1859609;
3.https://blog.csdn.net/howinfun/article/details/79271404。

二:对get请求中的Instant类型
1.定义全局Instant日期类型转换器:

package com.mycompany.myapp.config;

import java.text.ParseException;
import java.time.Instant;
import java.util.Date;

import org.apache.commons.lang3.StringUtils;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

import com.mycompany.myapp.utils.DateFormatUtil;

/**
* @author xuzhipeng
* @date 2019-03-20 18:02:41
* @since 1.0
*/
@Component
public class InstantConverter implements Converter {

    @Override
    public Instant convert(String source) {
        String dateString = source.trim();
        if(StringUtils.isNotBlank(dateString)){
            Date pareDate;
            try {
                pareDate = DateFormatUtil.pareDate(dateString);
                if(null != pareDate){
                    return pareDate.toInstant();
                }
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

}

2.springboot2.0以上只配置全局转换器不生效,需要加以下代码:

package com.mycompany.myapp.config;

import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.stereotype.Component;

/**
 * @author xuzhipeng
 * @date 2019-03-22 16:04:49
 * @since 1.0
 */
@Component
public class SpringContextListener implements ApplicationListener {
    @Autowired
    private Set> converters;
    @Autowired
    private ConversionService conversionService;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        GenericConversionService gcs = (GenericConversionService) conversionService;
        for (Converter converter : converters) {
            gcs.addConverter(converter);
        }
    }
}

参考:
1.https://github.com/spring-projects/spring-boot/issues/6222;
2.https://github.com/spring-projects/spring-framework/issues/11081;
3.https://blog.csdn.net/qq_31871785/article/details/72863289。

三:实体类中的Instant类型返回yyyy-MM-dd格式:

package com.mycompany.myapp.config;

import java.io.IOException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

/**
 * @author xuzhipeng
 * @date 2019-03-26 14:36:44
 * @since 1.0
 */
public class CustomInstantDateSerializer extends StdSerializer {

    private static final long serialVersionUID = 1L;

    private static DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    public CustomInstantDateSerializer() {
        this(null);
    }

    public CustomInstantDateSerializer(Class t) {
        super(t);
    }

    @Override
    public void serialize(Instant instant, JsonGenerator jsonGenerator, SerializerProvider provider)
            throws IOException {
        if (instant == null) {
            return;
        }
        String jsonValue = format.format(instant.atZone(ZoneId.systemDefault()));
        jsonGenerator.writeString(jsonValue);
    }
}
public class Event {
    public String name;
 
    @JsonSerialize(using = CustomInstantDateSerializer.class)
    public Instant eventDate;
}

参考:
1.https://www.baeldung.com/jackson-serialize-dates;

你可能感兴趣的:(SpringBoot对接收及返回Instant类型的处理)