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) 方法指定加入列表的位置:

 

  如果本文對你有幫助,記得點個【推薦】哦♥


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM