源码分析之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) {}

原文地址:https://www.cnblogs.com/756623607-zhang/p/10312178.html

时间: 2024-10-03 20:18:20

源码分析之FastJson全局配置日期格式导致@JSONField(format = "yyyy-MM-dd")注解失效的相关文章

PHP源码分析之session.auto_start配置分析

作者:zhanhailiang 日期:2014-10-20 应用分析 日常开发中,php.ini配置session.auto_start=0默认关闭会话时如果想开启会话需要调用session_start: <?php   session_start(); //... 内核分析 通过查到源码,可知session_start定义如下: 1881 /* {{{ proto bool session_start(void) 1882 Begin session - reinitializes freez

dubbo源码分析7——dubbo的配置解析_与spring的整合

dubbo的配置其实就是建立在spring的命名空间的配置机制之上的.在dubbo的jar包的META-INF目录下会有spring.handlers这个文件,用来配置spring的命名空间和解析类的对应关系.打开spring.handlers文件,可知dubbo的命名空间配置的处理类为com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler,代码: public void init() { registerBeanDefiniti

《kernel源码分析(一)配置和编译过程》

1.内核的配置和编译 cp arch/arm/configs/xx_defconfig .config make menuconfig make uImage 2.了解内核的配置过程和编译过程 在内核的配置过程中,会生成文件.config. 以网卡DM9000为例: 在.config中可以找到”CONFIG_DM9000=y“,这表示这个模块会被编译进内核.“CONFIG_DM9000=m”,这表示模块会被编译成.ko文件,可以动态的加载到内核. 在内核顶层目录对CONFIG_DM9000进行查

[Abp 源码分析]四、模块配置

0.简要介绍 在 Abp 框架当中通过各种 Configuration 来实现模块的配置,Abp 本身提供的很多基础设施功能的一些在运行时的行为是通过很多不同的 Configuration 来开放给用户进行一些自定义配置的. 比如说缓存模块,我要配置缓存的过期时间,Abp 默认是 1 个小时,但是我也可以自己来定义,直接赋值或者从配置项来读取都是由具体使用者来控制的,所以 Abp 通过各种 Configuration 类来控制一些运行时参数. 这些 Abp 本身基础设施的配置类都是存放在 \Ab

faac源码分析之解码参数配置

FAAC定义了一个结构体用来定义解码器的工作解码参数,该结构体的定义如下所示: typedef struct faacEncConfiguration { /* config version */ int version; /* library version */ char *name; /* copyright string */ char *copyright; /* MPEG version, 2 or 4 */ unsigned int mpegVersion; /* AAC obje

STL源码分析--第二级空间配置器

本文讲解SGI STL空间配置器的第二级配置器. 相比第一级配置器,第二级配置器多了一些机制,避免小额区块造成内存的碎片.不仅仅是碎片的问题,配置时的额外负担也是一个大问题.因为区块越小,额外负担所占的比例就越大. 额外负担是指动态分配内存块的时候,位于其头部的额外信息,包括记录内存块大小的信息以及内存保护区(判断是否越界).要想了解详细信息,请参考MSVC或者其他malloc实现. SGI STL第二级配置器具体实现思想 如下: 如果要分配的区块大于128bytes,则移交给第一级配置器处理.

SpringMVC拦截器详解[附带源码分析]

目录 前言 重要接口及类介绍 源码分析 拦截器的配置 编写自定义的拦截器 总结 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:http://www.cnblogs.com/fangjian0423/p/springMVC-introduction.html 拦截器是每个Web框架必备的功能,也是个老生常谈的主题了. 本文将分析SpringMVC的拦截器功能是如何设计的,让读者了解该功能设计的原理. 重要接口及类介绍 1. Hand

SpringMVC关于json、xml自动转换的原理研究[附带源码分析 --转

SpringMVC关于json.xml自动转换的原理研究[附带源码分析] 原文地址:http://www.cnblogs.com/fangjian0423/p/springMVC-xml-json-convert.html 目录 前言 现象 源码分析 实例讲解 关于配置 总结 参考资料 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:http://www.cnblogs.com/fangjian0423/p/springMVC-in

jQuery.lazyload使用及源码分析

前言: 貌似以前自己也写过图片懒加载插件,但是新公司使用的是jQuery.lazyload插件,为了更好的运用,自己还是把源码看了遍,分别记录了如何使用, 插件原理,各个配置属性的完整解释,demo实例,源码分析(较简短),源码分析可以配合使用,配置属性,原理进行阅读,如需转载,请注明出处 博客园 华子yjh 一.如何使用 // 最简单的使用,不带参数 $('img').lazyload(); // 带参数(配置对象),下面配置对象中的各个属性值都是默认的 $('img').lazyload({