zoukankan      html  css  js  c++  java
  • 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"

      另外对于高流量的服务站点,似乎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) 方法指定加入列表的位置:

     

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

  • 相关阅读:
    分类算法 学习笔记
    机器学习概述 & 特征工程 学习笔记
    Java基础知识
    牛客中Java工程师模拟面试整理
    leetcode142. 环形链表 II
    面经中的题目整理
    面经总结
    软件设计师补题(2007下半年上午题)
    软件设计师补题(2005上半年上午题)
    软件设计师补题(2005下半年上午题)
  • 原文地址:https://www.cnblogs.com/xuruiming/p/13143271.html
Copyright © 2011-2022 走看看