• 【源码分析】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) {}
  • 相关阅读:
    hihoCoder#1142(三分求极值)
    hihoCoder#1095(二分搜索)
    hihoCoder#1139(二分+bfs)
    java亦或(^)
    JAVA线程
    java中io流浅析
    java循环
    java集合练习
    JAVA集合
    java面向对象(串)
  • 原文地址:https://www.cnblogs.com/756623607-zhang/p/10312178.html
Copyright © 2020-2023  润新知