出現的問題
我全局配置的時間格式是: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) {}