10、SpringMVC进阶:使用fastjson自定义消息转换器

前置知识

Content-Type

Content-Type(内容类型),常作用于HTTP消息头,表示内容编码类型。可以根据编码类型使用特定的解析方式,获取数据流中的数据。

*

Media Type

Media Type,即媒体类型,也叫做于MIME类型,有时在一些协议的消息头中叫做“Content-Type”。它使用两部分标识符来确定一个类型。在HTTP中,消息头Content-Type对应的值就可以叫做是Media Type。

Media Type对象包含了三种信息:type 、subtype、charset,比如下面表示这个数据流的媒体类型为:

  • type值是text,表示是文本这一大类;
  • / 后面的x-markdown是subtype,表示是文本这一大类下的markdown这一小类;
  • charset=utf-8 则表示采用UTF-8编码
text/x-markdown; charset=utf-8

在Spring中,提供了MediaType类来表示媒体类型,提供了MediaType 类型的各种常量、字符串常量,表示各种媒体类型。

  private static final long serialVersionUID = 2069937152339670231L;
    //  */* 支持所有的媒体类型
    public static final MediaType ALL = new MediaType("*", "*");
    public static final String ALL_VALUE = "*/*";
    // Application:用于传输应用程序数据或者二进制数据;
    // application/atom+xml Atom XML聚合格式   
    public static final MediaType APPLICATION_ATOM_XML = new MediaType("application", "atom+xml");
    public static final String APPLICATION_ATOM_XML_VALUE = "application/atom+xml";
    // application/cbor  cbor类型二进制形式JSON格式
    public static final MediaType APPLICATION_CBOR = new MediaType("application", "cbor");
    public static final String APPLICATION_CBOR_VALUE = "application/cbor";
    // application/x-www-form-urlencoded  即表单默认的数据提交格式,提交的数据以key=val形式编码,用&连接,如
    public static final MediaType APPLICATION_FORM_URLENCODED = new MediaType("application", "x-www-form-urlencoded");
    public static final String APPLICATION_FORM_URLENCODED_VALUE = "application/x-www-form-urlencoded";
    // application/json JSON数据格式
    public static final MediaType APPLICATION_JSON = new MediaType("application", "json");
    public static final String APPLICATION_JSON_VALUE = "application/json";
    // application/json;charset=UTF-8  指定为JSON格式,以UTF-8字符编码进行编码
    /** @deprecated */
    @Deprecated
    public static final MediaType APPLICATION_JSON_UTF8;
    /** @deprecated */
    @Deprecated
    public static final String APPLICATION_JSON_UTF8_VALUE = "application/json;charset=UTF-8";
    //   application/octet-stream  二进制流数据(如常见的文件下载)
    public static final MediaType APPLICATION_OCTET_STREAM;
    public static final String APPLICATION_OCTET_STREAM_VALUE = "application/octet-stream";
    // application/pdf  pdf格式 
    public static final MediaType APPLICATION_PDF;
    public static final String APPLICATION_PDF_VALUE = "application/pdf";
    // application/problem+json  错误信息Json格式
    public static final MediaType APPLICATION_PROBLEM_JSON;
    public static final String APPLICATION_PROBLEM_JSON_VALUE = "application/problem+json";
    // application/problem+json;charset=UTF-8  错误信息Json格式,以UTF-8字符编码进行编码
    /** @deprecated */
    @Deprecated
    public static final MediaType APPLICATION_PROBLEM_JSON_UTF8;
    /** @deprecated */
    @Deprecated
    public static final String APPLICATION_PROBLEM_JSON_UTF8_VALUE = "application/problem+json;charset=UTF-8";
    // application/problem+xml 错误信息XML格式
    public static final MediaType APPLICATION_PROBLEM_XML;
    public static final String APPLICATION_PROBLEM_XML_VALUE = "application/problem+xml";
    // application/rss+xml RSS格式的XML文件
    public static final MediaType APPLICATION_RSS_XML;
    public static final String APPLICATION_RSS_XML_VALUE = "application/rss+xml";
    // application/x-ndjson  ndJSON的Json标准
    public static final MediaType APPLICATION_NDJSON;
    public static final String APPLICATION_NDJSON_VALUE = "application/x-ndjson";
    /** @deprecated */
    // application/stream+json  JSON数据流
    @Deprecated
    public static final MediaType APPLICATION_STREAM_JSON;
    /** @deprecated */
    @Deprecated
    public static final String APPLICATION_STREAM_JSON_VALUE = "application/stream+json";
    // application/xhtml+xml XHTML系列文档的类型
    public static final MediaType APPLICATION_XHTML_XML;
    public static final String APPLICATION_XHTML_XML_VALUE = "application/xhtml+xml";
    // application/xml  XML数据格式
    public static final MediaType APPLICATION_XML;
    public static final String APPLICATION_XML_VALUE = "application/xml";
	
	// 图片相关
	// image/gif gif图片格式
    public static final MediaType IMAGE_GIF;
    public static final String IMAGE_GIF_VALUE = "image/gif";
    // image/jpeg jpg图片格式
    public static final MediaType IMAGE_JPEG;
    public static final String IMAGE_JPEG_VALUE = "image/jpeg";
    // image/png png图片格式
    public static final MediaType IMAGE_PNG;
    public static final String IMAGE_PNG_VALUE = "image/png";
	
	// Multipart:用于连接消息体的多个部分构成一个消息,这些部分可以是不同类型的数据
    // multipart/form-data 会将表单的数据处理为一条消息,以标签为单元,用分隔符分开。既可以上传键值对,也可以上传文件
    public static final MediaType MULTIPART_FORM_DATA;
    public static final String MULTIPART_FORM_DATA_VALUE = "multipart/form-data";
    // multipart/mixed  变成 Stream 流的方式
    public static final MediaType MULTIPART_MIXED;
    public static final String MULTIPART_MIXED_VALUE = "multipart/mixed";
    // multipart/related 多种格式
    public static final MediaType MULTIPART_RELATED;
    public static final String MULTIPART_RELATED_VALUE = "multipart/related";

	// Text:用于标准化地表示的文本信息,文本消息可以是多种字符集和或者多种格式
	//  text/event-stream 事件流
    public static final MediaType TEXT_EVENT_STREAM;
    public static final String TEXT_EVENT_STREAM_VALUE = "text/event-stream";
    // text/html  html格式文本
    public static final MediaType TEXT_HTML;
    public static final String TEXT_HTML_VALUE = "text/html";
    // ext/markdown markdown格式文本
    public static final MediaType TEXT_MARKDOWN;
    public static final String TEXT_MARKDOWN_VALUE = "text/markdown";
    //  text/plain 纯文本格式
    public static final MediaType TEXT_PLAIN;
    public static final String TEXT_PLAIN_VALUE = "text/plain";
    // text/xml XML格式文本
    public static final MediaType TEXT_XML;
    public static final String TEXT_XML_VALUE = "text/xml";

HttpMessageConverter使用fastjson

Spring MVC提供了很多消息转换器,在之前消息转换源码中我们分析到报文是由MappingJackson2HttpMessageConverter转换器进行转换的,其使用的是Jackson 2.X。

如果我们想使用fastjson该如何设置呢?

思路

默认转换器

之前分析,MVC会添加十个默认的转换器,我们了解下具体都是干嘛的,以下表格按照顺序排列。

转换器 支持的MediaType 默认字符集 作用
ByteArrayHttpMessageConverter application/octet-stream;* / * 读写字节数组
StringHttpMessageConverter text/plain;* / * UTF-8 读写字符串
StringHttpMessageConverter text/plain;* / * ISO-8859-1 读写字符串
ResourceHttpMessageConverter * / * 可以读/写 {@link Resource Resources} ,并支持字节范围请求
ResourceRegionHttpMessageConverter * / * 可以读取单个 {@link ResourceRegion} 或 {@link ResourceRegion } 的集合。
SourceHttpMessageConverter application/xml;text/xml;application/*+xml 可以读写 {@link Source} 对象
AllEncompassingFormHttpMessageConverter application/x-www-form-urlencoded;multipart/form-data;multipart/mixed UTF-8 添加对基于 XML 和 JSON 的部分的支持。对FormHttpMessageConverter的扩展
MappingJackson2HttpMessageConverter application/json;application/*+json Jackson 2.x 读取和 写入 JSON {@link ObjectMapper}。
MappingJackson2HttpMessageConverter 重复了 重复了 重复了
Jaxb2RootElementHttpMessageConverter application/xml;text/xml;application/*+xml 可以使用 JAXB2 读写 XML
WebMvcConfigurer配置类

spring MVC提供了WebMvcConfigurer接口,可以自定义配置类,实现configureMessageConverters或者extendMessageConverters就可以添加自定义消息转换器了。

	/**
	* 配置 {@link HttpMessageConverter HttpMessageConverter} 用于从请求报文读取和写入响应报文。 
	* 默认情况下,只要类路径中存在相应的第三方库,例如 Jackson JSON、JAXB2 和其他库,就会配置所有内置转换器。
	* 使用此方法会关闭默认转换器注册。
	 */
	default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
   
     
	}

	/**
	 * 扩展或修改转换器列表,请注意,转换器注册的顺序很重要。
	 * 特别是在客户端接受 {@link org.springframework.http.MediaType#ALL} 的情况下更早配置的转换器将是首选。
	 */
	default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
   
     
	}

FastJsonHttpMessageConverter类

在fastjson中,默认已经实现了一个HttpMessageConverter消息转换器,直接拿来就可以使用了。
*
在FastJsonHttpMessageConverter,还可以设置FastJsonConfig,提供了更加丰富的功能。

我们可以通过FastJsonConfig配置序列化过滤器、序列化特性、特殊字段序列化等功能。

*
FastJsonConfig还可以配置SerializerFeature,用于对响应数据进行特殊处理,比如常用的有,时间格式化、null转空、Long转字符串等。

SerializerFeature类配置项如下:

public enum SerializerFeature {
   
     
	// 输出key时是否使用双引号,默认为true
    QuoteFieldNames,
    // 使用单引号,默认为false 
    UseSingleQuotes,
    // 是否输出值为null的字段,默认为false 
    WriteMapNullValue,
    // 使用ToString写入枚举,默认为false 
    WriteEnumUsingToString,
    // 使用名称写入枚举,默认为false 
    WriteEnumUsingName,
    // Date使用ISO8601格式输出,默认为false
    UseISO8601DateFormat,
    // List字段如果为null,输出为[],而非null 
    WriteNullListAsEmpty,
    // 字符类型字段如果为null,输出为"",而非null
    WriteNullStringAsEmpty,
    // 数值字段如果为null,输出为0,而非null 
    WriteNullNumberAsZero,
    // Boolean字段如果为null,输出为false,而非null
    WriteNullBooleanAsFalse,
    // 如果是true,类中的Get方法对应的Field是transient,序列化时将会被忽略。默认为true
    SkipTransientField,
    // 按字段名称排序后输出。默认为false
    SortField,
    /** @deprecated */
    @Deprecated
    // 把\t做转义输出,默认为false
    WriteTabAsSpecial,
    // 结果是否格式化,默认为false
    PrettyFormat,
    // 序列化时写入类型信息,默认为false。反序列化是需用到
    WriteClassName,
    // 消除对同一对象循环引用的问题,默认为false
    DisableCircularReferenceDetect,
    // 对斜杠'/'进行转义
    WriteSlashAsSpecial,
    // 将中文都会序列化为\uXXXX格式,字节数会多一些,但是能兼容IE 6,默认为false
    BrowserCompatible,
    // 全局修改日期格式,默认为false。JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd";JSON.toJSONString(obj, SerializerFeature.WriteDateUseDateFormat);
    WriteDateUseDateFormat,
    // 不打印最外层根对象的类名,例如一个DO里面引入了另一个DO,外层DO不会打印类名
    NotWriteRootClassName,
    /** @deprecated */
    // 一个对象的字符串属性中如果有特殊字符如双引号,将会在转成json时带有反斜杠转移符。如果不需要转义,可以使用这个属性。默认为false
    DisableCheckSpecialChar,
    // 把Bean转换成数组
    BeanToArray,
    // 如果key不为String 则转换为String 比如Map的key为Integer
    WriteNonStringKeyAsString,
    // 将基础类型的默认值屏蔽
    NotWriteDefaultValue,
    // 防御xss安全攻击
    BrowserSecure,
    // 不是类 field 字段对应的getter 方法会被忽略
    IgnoreNonFieldGetter,
    // 是把不是字符串形式的value转成字符串的形式
    WriteNonStringValueAsString,
    // 用于忽略那些抛错的getter方法
    IgnoreErrorGetter,
    // BigDecimal使用科学计数法
    WriteBigDecimalAsPlain,
    // Map做排序再输出
    MapSortField;
}

案例演示

1. 添加fastjson包
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.78</version>
        </dependency>

2. 添加配置类

自定义配置类实现WebMvcConfigurer 接口,主要有以下几步:

  • 清理默认的转换器,重新添加,因为扩展的话,要排序什么的,比较麻烦
  • 参考WebMvcConfigurationSupport类添加一些必须的转转器,
  • 添加FastJson转换器FastJsonHttpMessageConverter ,指定支持的MediaType并添加相关配置
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
   
     

    private static final boolean shouldIgnoreXml = SpringProperties.getFlag("spring.xml.ignore");

    /**
     * 自定义消息转换器,覆盖默认
     */
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
   
     
        // 清理默认
        messageConverters.clear();
        // 添加
        messageConverters.add(new ByteArrayHttpMessageConverter());
        messageConverters.add(new StringHttpMessageConverter());
        messageConverters.add(new ResourceHttpMessageConverter());
        messageConverters.add(new ResourceRegionHttpMessageConverter());
        if (!shouldIgnoreXml) {
   
     
            messageConverters.add(new SourceHttpMessageConverter<>());
        }
        messageConverters.add(new AllEncompassingFormHttpMessageConverter());
        // 配置FastJson并添加
        FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
        fastJsonHttpMessageConverter.setSupportedMediaTypes(getSupportedMediaTypes()); // 支持的MediaType
        fastJsonHttpMessageConverter.setDefaultCharset(StandardCharsets.UTF_8); // 默认字符集
        fastJsonHttpMessageConverter.setFastJsonConfig(getFastJsonConfig());
        messageConverters.add(fastJsonHttpMessageConverter);
    }
    /**
     * FastJson 配置
     */
    private FastJsonConfig getFastJsonConfig() {
   
     
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        // 配置序列化器功能
        fastJsonConfig.setSerializerFeatures(
                SerializerFeature.PrettyFormat,
                SerializerFeature.WriteNullStringAsEmpty,
                SerializerFeature.WriteNullListAsEmpty
        );
        // 序列化配置,将Long类型转为字符串
        //不要使用全局的,会影响其他地方的JSON序列化SerializeConfig serializeConfig = SerializeConfig.globalInstance;
        SerializeConfig serializeConfig =new SerializeConfig();
        serializeConfig.put(BigInteger.class, ToStringSerializer.instance);
        serializeConfig.put(Long.class, ToStringSerializer.instance);
        serializeConfig.put(Long.TYPE, ToStringSerializer.instance);
        serializeConfig.put(long.class, ToStringSerializer.instance);
        fastJsonConfig.setSerializeConfig(serializeConfig);
        return fastJsonConfig;
    }

    /**
     * 配置支持的媒体类型
     */
    private List<MediaType> getSupportedMediaTypes() {
   
     
        List<MediaType> supportedMediaTypes = Lists.newArrayList();
        supportedMediaTypes.add(MediaType.APPLICATION_JSON);
        supportedMediaTypes.add(new MediaType("application", "*+json"));
        return supportedMediaTypes;
    }
}

3. 测试

在AbstractMessageConverterMethodProcessor的writeWithMessageConverters打上断点。

可以看到在读写的时候,使用的是FastJsonHttpMessageConverter。

*

版权声明:本文不是「本站」原创文章,版权归原作者所有 | 原文地址: