Spring Boot中fastjson的@JSONField(format = "yyyy-MM-dd HH:mm:ss")失效可能原因

  Spring Boot 2.x中目前自带的JSON序列化和反序列化工具主要有:com.google.gson.Gson、com.fasterxml.jackson和org.yaml.snakeyaml.Yaml(用于yaml与json的转换):

  平常的Web开发SpringBoot自带JSON包里用的最多的应该是Jackson了,但是毕竟它是国外产的,在某些方面跟我们中国人很不合调!比如日期时间的序列化与反序列化方面,默认情况下Jackson无法将“yyyy-MM-ddTHH:mm:ss.SSSZ”和“EEE, dd MMM yyyy HH:mm:ss z”这样的时间字符串转换为“yyyy-MM-dd HH:mm:ss”,因为前面那两种时间格式很多欧美国家都看习惯了,没有过多的考虑将它们转换成咱中国人习惯的“yyyy-MM-dd HH:mm:ss”格式。但是很不巧,目前绝大多数流行的浏览器内核都是这些欧美国家产的,从这些浏览器上原生产出来的日期时间的字符串格式几乎都是“yyyy-MM-ddTHH:mm:ss.SSSZ”和“EEE, dd MMM yyyy HH:mm:ss z”这样的~~~这个时候想用Jackson转成“yyyy-MM-dd HH:mm:ss”格式往往就有些麻烦了:

JSON parse error: Cannot deserialize value of type `java.util.Date` 
from String \"2020-06-16T04:53:49.363Z\": expected format \"yyyy-MM-dd HH:mm:ss\";
nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date`
from String \"2020-06-16T04:53:49.363Z\": expected format \"yyyy-MM-dd HH:mm:ss\"\n

  另外对于高流量的服务站点,似乎Jackson序列化与反序列化的性能也不是非常好,于是国内阿里云的兄弟们搞了个咱中国自产的JSON序列化与反序列化工具——Fastjson

  这个工具把上面那种日期时间转换的问题都解决了,并且性能也跟它的名字一样fast!Github地址:https://github.com/alibaba/fastjson

  然而,在SpringBoot 2.x中使用它的时候要注意了,像本文开头看到的那样,SpringBoot 2.x中默认并没人集成Fastjson,需要通过配置将Fastjson加入到Http消息转换器列表中。这里将Fastjson加入到Http消息转换器列表的时机(或者叫方法)就有两种了:

  第一种:在Spring Boot的Http消息转换器列表的Bean生成时就将Fastjson的Http消息转换器加入其中,这种方式最保险、最靠谱,也能第一时间将Fastjson的Http消息转换器加入:

/**
 * 添加与WebMvc相关的自定义配置
 *
 * @author 707669522@qq.com
 * @since 2020-06-11
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    /**
     * 将FastJsonHttpMessageConverter包装成HttpMessageConverter对象,并用其参与消息转换器管道
     * 列表HttpMessageConverters的初始化和Bean的生成,该方式会将得到的HttpMessageConverters中
     * 的httpMessageConverter作为生成消息转换器管道列表的初始数据(从源码得知会早于默认转换器),
     * 因此也会早于通过实现configureMessageConverters接口向消息转换器管道列表追加转换器的方式
     *
     * @return 最终版的Http消息转换器列表对象
     */
    @Bean
    public HttpMessageConverters fastJsonConverters() {
        FastJsonHttpMessageConverter fastJsonConverter = new FastJsonHttpMessageConverter();
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
        fastJsonConverter.setFastJsonConfig(fastJsonConfig);
        HttpMessageConverter<?> httpMessageConverter = fastJsonConverter;
        return new HttpMessageConverters(httpMessageConverter);
    }
}

  new HttpMessageConverters(httpMessageConverter) 对应的Spring Boot源码 :

    public HttpMessageConverters(HttpMessageConverter<?>... additionalConverters) {
        this(Arrays.asList(additionalConverters));
    }

    public HttpMessageConverters(Collection<HttpMessageConverter<?>> additionalConverters) {
        this(true, additionalConverters);
    }

    public HttpMessageConverters(boolean addDefaultConverters, Collection<HttpMessageConverter<?>> converters) {
        List<HttpMessageConverter<?>> combined = getCombinedConverters(converters,
                addDefaultConverters ? getDefaultConverters() : Collections.emptyList());
        combined = postProcessConverters(combined);
        this.converters = Collections.unmodifiableList(combined);
    }

    private List<HttpMessageConverter<?>> getCombinedConverters(Collection<HttpMessageConverter<?>> converters,
            List<HttpMessageConverter<?>> defaultConverters) {
        List<HttpMessageConverter<?>> combined = new ArrayList<>();
        List<HttpMessageConverter<?>> processing = new ArrayList<>(converters);
        for (HttpMessageConverter<?> defaultConverter : defaultConverters) {
            Iterator<HttpMessageConverter<?>> iterator = processing.iterator();
            while (iterator.hasNext()) {
                HttpMessageConverter<?> candidate = iterator.next();
                if (isReplacement(defaultConverter, candidate)) {
                    combined.add(candidate);
                    iterator.remove();
                }
            }
       //默认的Converter在后面才加入 combined.add(defaultConverter);
if (defaultConverter instanceof AllEncompassingFormHttpMessageConverter) { configurePartConverters((AllEncompassingFormHttpMessageConverter) defaultConverter, converters); } } combined.addAll(0, processing); return combined; }

 

 

  第二种:通过实现WebMvcConfigurer接口的消息转换器配置方法configureMessageConverters来将Fastjson的Http消息转换器加入SpringBoot的Http消息转换器列表,这种方式属于后期追加的方式,因为这个时候SpringBoot的Http消息转换器列表已经生成,并且列表中已经有了默认的消息转换器:

/**
 * 添加与WebMvc相关的自定义配置
 *
 * @author 707669522@qq.com
 * @since 2020-06-11
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    /**
     * 实现消息转换器配置方法,向当前的Http消息转换器列表增加FastJsonHttpMessageConverter
     *
     * @param converters 当前的Http消息转换器列表对象
     */
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        FastJsonHttpMessageConverter fastJsonConverter = new FastJsonHttpMessageConverter();
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
        fastJsonConverter.setFastJsonConfig(fastJsonConfig);
        //converters.add(fastJsonConverter);//这会让fastJsonConverter排在消息转换器管道列表的最后,可能会轮不到它处理消息转换
        converters.add(0, fastJsonConverter);//要显示指明将fastJsonConverter排在消息转换器管道列表的首位
    }
}

   采用这种方法配置的同学一定要注意一下有个天坑,就是粗体注释所描述的:如果直接不指明FastJsonHttpMessageConverter将加入的列表位置,它默认会被加在列表最后,比默认的Jackson的位置还要靠后。这就会有问题了,当接收到格式为“yyyy-MM-ddTHH:mm:ss.SSSZ”和“EEE, dd MMM yyyy HH:mm:ss z”的日期时间JSON字符时,Jackson会直接处理掉,而轮不到Fastjson处理,最后造成@JSONField(format = "yyyy-MM-dd HH:mm:ss")失效!所以采用这种方式一定要用 add(int index, E element) 方法指定加入列表的位置:

 

  如果本文对你有帮助,记得点个【推荐】哦♥

posted @ 2020-06-16 17:36  岁月已走远  阅读(11698)  评论(0编辑  收藏  举报