【源码分析】FastJson全局配置日期格式导致@JSONField(format = "yyyy-MM-dd")注解失效
出现的问题
我全局配置的时间格式是:yyyy-MM-dd HH:mm:ss
@JSONField注解配置的时间格式是:yyyy-MM-dd
最终的返回结果是:yyyy-MM-dd HH:mm:ss
问题:为啥不是以注解定义的时间格式为主呢?
先说答案,后面再分析:
FastJson的全局配置日期格式会导致@JSONField注解失效
使用建议:
1.若全局配置了日期格式,就不要使用@JSONField注解
2.若想使用@JSONField注解,就不要全局配置日期格式
一、FastJson全局配置代码如下
@Configuration
public class FastJsonConverterConfig { @Bean
public HttpMessageConverters fastJsonHttpMessageConverters() {
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(
SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteNullListAsEmpty,
SerializerFeature.WriteNullStringAsEmpty,
SerializerFeature.WriteNullBooleanAsFalse
// SerializerFeature.WriteDateUseDateFormat
);
fastConverter.setFastJsonConfig(fastJsonConfig); //全局指定了日期格式
fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss"); //该设置目的,为了兼容jackson
fastConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON,MediaType.APPLICATION_JSON_UTF8,MediaType.APPLICATION_OCTET_STREAM));
HttpMessageConverter<?> converter = fastConverter;
return new HttpMessageConverters(converter);
}
}
二、使用@JSONField注解的Java Bean代码如下
@Data
public class UserCardInfoResponseModel { @JSONField(format = "yyyy-MM-dd")
private Date validStartDate; }
三、源码分析
1.首先我们看下FastJson最终格式化字段值的方法,JSONSerializer.writeWithFormat(Object object, String format)
public class JSONSerializer extends SerializeFilterable {
/**
* format就是@JSONField注解中指定的format值
* object就是需要格式化的变量
*/
public final void writeWithFormat(Object object, String format) {
if (object instanceof Date) {
//从当前类获取一个DateFormat,DateFormat就是用来格式化日期的类,再看看this.getDateFormat();的实现
DateFormat dateFormat = this.getDateFormat();
if (dateFormat == null) {
//只有当,当前类中的dateFormat为null时,才会使用JSONField注解中的format值初始化一个新的DateFormat
//那么我们可以肯定@JSONField注解没生效的原因就是,当前类中的dateFormat不为null
dateFormat = new SimpleDateFormat(format, locale);
dateFormat.setTimeZone(timeZone);
}
String text = dateFormat.format((Date) object);
out.writeString(text);
return;
}
if (object instanceof byte[]) {
byte[] bytes = (byte[]) object;
if ("gzip".equals(format) || "gzip,base64".equals(format)) {
GZIPOutputStream gzipOut = null;
try {z
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
if (bytes.length < 512) {
gzipOut = new GZIPOutputStream(byteOut, bytes.length);
} else {
gzipOut = new GZIPOutputStream(byteOut);
}
gzipOut.write(bytes);
gzipOut.finish();
out.writeByteArray(byteOut.toByteArray());
} catch (IOException ex) {
throw new JSONException("write gzipBytes error", ex);
} finally {
IOUtils.close(gzipOut);
}
} else if ("hex".equals(format)) {
out.writeHex(bytes);
} else {
out.writeByteArray(bytes);
}
return;
}
if (object instanceof Collection) {
Collection collection = (Collection) object;
Iterator iterator = collection.iterator();
out.write('[');
for (int i = 0; i < collection.size(); i++) {
Object item = iterator.next();
if (i != 0) {
out.write(',');
}
writeWithFormat(item, format);
}
out.write(']');
return;
}
write(object);
}
public DateFormat getDateFormat() {
if (dateFormat == null) {
if (dateFormatPattern != null) {
//第一次调用该方法时,dateformat为null,满足第一个if条件
//那么只有当dateFormatPattern有值时,才会初始化一个dateFormat对象,
//那么问题来了,dateFormatPattern值是从哪里来的呢,答案就是从我们的全局配置中来的,我们继续往下看
dateFormat = new SimpleDateFormat(dateFormatPattern, locale);
dateFormat.setTimeZone(timeZone);
}
}
return dateFormat;
}
}
2.其次,我们来看使用我们FastJson全局配置的地方,FastJsonHttpMessageConverter.writeInternal(Object object, HttpOutputMessage outputMessage);
public class FastJsonHttpMessageConverter extends AbstractHttpMessageConverter<Object>//
implements GenericHttpMessageConverter<Object> { @Override
protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { ByteArrayOutputStream outnew = new ByteArrayOutputStream();
try {
HttpHeaders headers = outputMessage.getHeaders(); //获取全局配置的filter
SerializeFilter[] globalFilters = fastJsonConfig.getSerializeFilters();
List<SerializeFilter> allFilters = new ArrayList<SerializeFilter>(Arrays.asList(globalFilters)); boolean isJsonp = false; //不知道为什么会有这行代码, 但是为了保持和原来的行为一致,还是保留下来
Object value = strangeCodeForJackson(object); if (value instanceof FastJsonContainer) {
FastJsonContainer fastJsonContainer = (FastJsonContainer) value;
PropertyPreFilters filters = fastJsonContainer.getFilters();
allFilters.addAll(filters.getFilters());
value = fastJsonContainer.getValue();
} //revise 2017-10-23 ,
// 保持原有的MappingFastJsonValue对象的contentType不做修改 保持旧版兼容。
// 但是新的JSONPObject将返回标准的contentType:application/javascript ,不对是否有function进行判断
if (value instanceof MappingFastJsonValue) {
if(!StringUtils.isEmpty(((MappingFastJsonValue) value).getJsonpFunction())){
isJsonp = true;
}
} else if (value instanceof JSONPObject) {
isJsonp = true;
} //我们看这里,fastJsonConfig就是我们全局配置的配置类,
//fastJsonConfig.getDateFormat()获取的就是我们全局配置的时间格式yyyy-MM-dd HH:mm:ss,然后我们看看JSON.writeJSONString方法
int len = JSON.writeJSONString(outnew, //
fastJsonConfig.getCharset(), //
value, //
fastJsonConfig.getSerializeConfig(), //
//fastJsonConfig.getSerializeFilters(), //
allFilters.toArray(new SerializeFilter[allFilters.size()]),
fastJsonConfig.getDateFormat(), //
JSON.DEFAULT_GENERATE_FEATURE, //
fastJsonConfig.getSerializerFeatures()); if (isJsonp) {
headers.setContentType(APPLICATION_JAVASCRIPT);
} if (fastJsonConfig.isWriteContentLength()) {
headers.setContentLength(len);
} outnew.writeTo(outputMessage.getBody()); } catch (JSONException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
} finally {
outnew.close();
}
}
}
3.然后,我们看看初始化我们第1步说到类JSONSerializer的地方JSON.writeJSONString(....)
public abstract class JSON implements JSONStreamAware, JSONAware {
public static final int writeJSONString(OutputStream os, //
Charset charset, //
Object object, //
SerializeConfig config, //
SerializeFilter[] filters, //
String dateFormat, //
int defaultFeatures, //
SerializerFeature... features) throws IOException {
SerializeWriter writer = new SerializeWriter(null, defaultFeatures, features);
try {
//看这里,就是用来初始化JSONSerializer对象
JSONSerializer serializer = new JSONSerializer(writer, config);
if (dateFormat != null && dateFormat.length() != 0) {
//然后在这里,将全局配置的日期格式dateFormat,设置到JSONSerializer中的
//到这里我们就应该很清楚整个的逻辑了
serializer.setDateFormat(dateFormat);
serializer.config(SerializerFeature.WriteDateUseDateFormat, true);
}
if (filters != null) {
for (SerializeFilter filter : filters) {
serializer.addFilter(filter);
}
}
serializer.write(object);
int len = writer.writeToEx(os, charset);
return len;
} finally {
writer.close();
}
}
}
四、保留下分析源码抓取的调用栈,方便下次阅读源码
WebMvcMetricsFilter\doFilterInternal
WebMvcMetricsFilter\filterAndRecordMetrics
ApplicationFilterChain\internalDoFilter
WsFilter\doFilter
ApplicationFilterChain\doFilter
ApplicationFilterChain\internalDoFilter
FrameworkServlet\service
FrameworkServlet\doGet
FrameworkServlet\processRequest
DispatcherServlet\doService()
DispatcherServlet\doDispatch\mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
AbstractHandlerMethodAdapter\handle(84)
RequestMappingHandlerAdapter\handleInternal(761)\mav = invokeHandlerMethod(request, response, handlerMethod);
RequestMappingHandlerAdapter\invokeHandlerMethod(835)
ServletInvocableHandlerMethod\invokeAndHandle(99)
HandlerMethodReturnValueHandlerComposite\handleReturnValue(75)
RequestResponseBodyMethodProcessor\handleReturnValue(171)
AbstractMessageConverterMethodProcessor\genericConverter.write(outputValue, declaredType, selectedMediaType, outputMessage);(272)
FastJsonHttpMessageConverter\super.write(o, contentType, outputMessage);(184)
AbstractHttpMessageConverter\writeInternal(t, outputMessage);(224)
FastJsonHttpMessageConverter\writeInternal(246)
**** JSON\writeJSONString(821)
===>
JSONSerializer\writeWithFieldName
===>
MapSerializer\write()
===>
JavaBeanSerializer\fieldSerializer.writeValue(serializer, propertyValue);
===>
FieldSerializer\serializer.writeWithFormat(propertyValue, format);
===>
**** JSONSerializer\public final void writeWithFormat(Object object, String format) {}
【源码分析】FastJson全局配置日期格式导致@JSONField(format = "yyyy-MM-dd")注解失效的更多相关文章
- MyCat源码分析系列之——配置信息和启动流程
更多MyCat源码分析,请戳MyCat源码分析系列 MyCat配置信息 除了一些默认的配置参数,大多数的MyCat配置信息是通过读取若干.xml/.properties文件获取的,主要包括: 1)se ...
- Web API 源码剖析之全局配置
Web API 源码剖析之全局配置 Web API 均指Asp.net Web API .本节讲述的是基于Web API 系统在寄宿于IIS. 本节主要讲述Web API全局配置.它是如何优雅的实现 ...
- 修改springfox-swagger源码,使example中时间格式默认为“yyyy-MM-dd HH:mm:ss”
修改swagger源码,使example中时间格式默认为"yyyy-MM-dd HH:mm:ss" 前言 简单点说,在swagger中,怎么能针对以下vo中的java.util.D ...
- Spring AMQP 源码分析 08 - XML 配置
### 准备 ## 目标 通过 XML 配置文件使用 Spring AMQP ## 前置知识 <Spring AMQP 源码分析 07 - MessageListenerAdapter> ...
- Flume-NG源码分析-整体结构及配置载入分析
在 http://flume.apache.org 上下载flume-1.6.0版本,将源码导入到Idea开发工具后如下图所示: 一.主要模块说明 flume-ng-channels 里面包含了fil ...
- STL源码分析之空间配置器
前言 SGI STL将new的申请空间和调用构造函数的两个功能分开实现, 如果对new不太清楚的, 可以先去看看这一篇new实现再来看配置器也不迟. 本节是STL分析的第一篇, 主要分析STL各个部分 ...
- Spring Boot 源码分析 数据源 + Mybatis 配置
公司今年开始使用 Spring Boot 开发,当然使用 Spring Boot 也是大势所趋,尤其是现在微服务的趋向,当然是选择基于Spring Boot 的 Spring Cloud.(所谓的 S ...
- 开源分布式数据库中间件MyCat源码分析系列
MyCat是当下很火的开源分布式数据库中间件,特意花费了一些精力研究其实现方式与内部机制,在此针对某些较为重要的源码进行粗浅的分析,希望与感兴趣的朋友交流探讨. 本源码分析系列主要针对代码实现,配置. ...
- [转]RMI方式Ehcache集群的源码分析
RMI方式Ehcache集群的源码分析 Ehcache不仅支持基本的内存缓存,还支持多种方式将本地内存中的缓存同步到其他使用Ehcache的服务器中,形成集群.如下图所示: Ehcache支持 ...
随机推荐
- 【Python31--pickle函数】
一.含义 1.pickle的实质是什么 答:利用一些算法把数据对象转换成“二进制文件”,存储在硬盘上,当然也可以放在数据库或者是另外一台计算机上 2.存放:picking,读取:unpicking 3 ...
- 同时import两个版本的QtQuick【1、2】,默认使用
在同一个qml文件中,如果同时import了Qtquick1和2,那么谁在后面,谁起作用
- 前端 --- 1 HTML
一.文档结构 <!DOCTYPE html> <html lang="zh-CN"> #这个lang表示语言,zh-CN是中文的意思, 如果以英文为主,就写 ...
- Apache下的ArrayUtils工具类总结
ArrayUtils中的方法: 1.add():将给定的数据添加到指定的数组中,返回一个新的数组. 2.addAll():合并两个数组. 3.contains():检查该数据在该数组中是否存在,返回一 ...
- Bootstrap3基础 img-rounded 图片的四个角改成圆角
内容 参数 OS Windows 10 x64 browser Firefox 65.0.2 framework Bootstrap 3.3.7 editor ...
- 快速自动安装dart
@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat Non ...
- Python之Requests的安装与基本使用
# 安装 使用 pip 安装Requests非常简单 pip install requests 或者使用 easy_install 安装 easy_install requests # 获得源码 Re ...
- .net core mvc 错误信息显示 ModelState.AddModelError
关于ModelState.AddModelError错误信息不在前端页面显示问题.经过一位高人指定终于知道了为什么,在次写着警示自己看文档一定要仔细.再次感谢这为兄弟 https://www.cnbl ...
- P3810 【模板】三维偏序(陌上花开)(cdq分治)
思路 看到这种偏序类的题目,而且不要求强制在线,可以立刻想到cdq分治 注意这题有一个问题,就是询问的是小于等于而不是小于,如果相等的话两个元素会相互贡献,而cdq的特点是右区间不能对左边有影响,所以 ...
- (转载)用C#实现MySQL建库及建表
最近做一个项目,为了方便用户使用,希望可以在系统初始化的时候,自动实现MySQL数据库的建库和建表操作.在网上查了很多资料都没有找到合适的,偶尔在一个国外网站上看到了相关的内容,特把实现方法整理如下: ...