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) 方法指定加入列表的位置:
如果本文对你有帮助,记得点个【推荐】哦♥