zoukankan      html  css  js  c++  java
  • 【源码分析】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(); } } }

    四、保留下分析源码抓取的调用栈,方便下次阅读源码

    WebMvcMetricsFilterdoFilterInternal
    WebMvcMetricsFilterfilterAndRecordMetrics
    ApplicationFilterChaininternalDoFilter
    WsFilterdoFilter
    ApplicationFilterChaindoFilter
    ApplicationFilterChaininternalDoFilter
    FrameworkServletservice
    FrameworkServletdoGet
    FrameworkServletprocessRequest
    DispatcherServletdoService()
    DispatcherServletdoDispatchmv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    AbstractHandlerMethodAdapterhandle(84)
    RequestMappingHandlerAdapterhandleInternal(761)mav = invokeHandlerMethod(request, response, handlerMethod);
    RequestMappingHandlerAdapterinvokeHandlerMethod(835)
    ServletInvocableHandlerMethodinvokeAndHandle(99)
    HandlerMethodReturnValueHandlerCompositehandleReturnValue(75)
    RequestResponseBodyMethodProcessorhandleReturnValue(171)
    AbstractMessageConverterMethodProcessorgenericConverter.write(outputValue, declaredType, selectedMediaType, outputMessage);(272)
    FastJsonHttpMessageConvertersuper.write(o, contentType, outputMessage);(184)
    AbstractHttpMessageConverterwriteInternal(t, outputMessage);(224)
    FastJsonHttpMessageConverterwriteInternal(246)
    ****  JSONwriteJSONString(821)
    ===>
    JSONSerializerwriteWithFieldName
    ===>
    MapSerializerwrite()
    ===>
    JavaBeanSerializerfieldSerializer.writeValue(serializer, propertyValue);
    ===>
    FieldSerializerserializer.writeWithFormat(propertyValue, format);
    ===>
    **** JSONSerializerpublic final void writeWithFormat(Object object, String format) {}
  • 相关阅读:
    【转】win8.1下安装ubuntu
    Codeforces 1025G Company Acquisitions (概率期望)
    Codeforces 997D Cycles in Product (点分治、DP计数)
    Codeforces 997E Good Subsegments (线段树)
    Codeforces 1188E Problem from Red Panda (计数)
    Codeforces 1284E New Year and Castle Building (计算几何)
    Codeforces 1322D Reality Show (DP)
    AtCoder AGC043C Giant Graph (图论、SG函数、FWT)
    Codeforces 1305F Kuroni and the Punishment (随机化)
    AtCoder AGC022E Median Replace (字符串、自动机、贪心、计数)
  • 原文地址:https://www.cnblogs.com/756623607-zhang/p/10312178.html
Copyright © 2011-2022 走看看