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) 方法指定加入列表的位置:
如果本文對你有幫助,記得點個【推薦】哦♥