更新于:2020-04-25
一、Introduce
Spring Boot provides integration with three JSON mapping libraries:
- Gson
- Jackson
- JSON-B
Jackson is the preferred and default library.
SpringBoot 默认提供了三种 json 支持,默认使用 Jackson 作为支持。
除了官方提到的这三种 json 框架,开发中常用的还有 Fast-Json 等。
二、Spring MVC 与 Json 解析
Spring-MVC请求参数和响应结果解析 中提到了,Spring MVC 框架
针对请求处理前会对请求入参进行参数处理,针对响应结果同样会进行响应的处理。
其中针对 JSON 格式入参和 JSON 格式返回的处理都会交由相关的 JSON 解析框架来完成。
下面以 SpringBoot 默认的 Jackson 解析框架进行分析。
一个简单的测试 API
2.1、Json 请求入参
请求入参会在 ⑦ 被解析器集合解析。
Json 入参对应的解析器为 RequestResponseBodyMethodProcessor。
RequestResponseBodyMethodProcessor 解析器的入参解析流程 UML 如下
Json 请求入参的解析委派给了 HttpMessageConverter 实现类来完成,jackson 对应的类为 MappingJackson2HttpMessageConverter
MappingJackson2HttpMessageConverter 类结构

MappingJackson2HttpMessageConverter 相关逻辑实现在其父类 AbstractJackson2HttpMessageConverter 中完成
AbstractJackson2HttpMessageConverter 相关代码
public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> {@Overridepublic boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) { return canRead(clazz, null, mediaType); }@Overridepublic boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {// 仅支持 application/json 和 application/*+json 格式if (!canRead(mediaType)) {return false;}JavaType javaType = getJavaType(type, contextClass);AtomicReference<Throwable> causeRef = new AtomicReference<>();// 能够被序列化if (this.objectMapper.canDeserialize(javaType, causeRef)) {return true;}logWarningIfNecessary(javaType, causeRef.get());return false;}@Overridepublic Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)throws IOException, HttpMessageNotReadableException {// 这里为入参对象的类型,如: DemoModelJavaType javaType = getJavaType(type, contextClass);// 调用 Jackson 原生方法进行参数转换return readJavaType(javaType, inputMessage);}private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {return this.objectMapper.readValue(inputMessage.getBody(), javaType);}}
以上逻辑主要两个方法
canRead
判定是否能够被该解析器解析 条件一:Content-Type 格式为:application/json 或 application/*+json 格式 条件二:映射的 Java 对象允许被反序列化。
read
直接使用 Jackson 原生的
ObjectMapperAPI
2.2、Json 响应结果解析
⑨ 得到响应结果后在 ⑩ 进行相关的响应结果类型解析。
Json 参数返回结果的解析器同请求入参解析器都为 RequestResponseBodyMethodProcessor。
RequestResponseBodyMethodProcessor 类结构如下:
通过类图,能够知道 RequestResponseBodyMethodProcessor 不仅仅支持请求入参解析,同样支持响应结果解析。
RequestResponseBodyMethodProcessor 解析器的响应结果解析流程 UML 如下
Json 响应结果的解析同样委派给了 HttpMessageConverter 实现类来完成,jackson 对应的类为 MappingJackson2HttpMessageConverter
不过不同的是 响应结果解析调用的是 write 方法。主要逻辑处理同样是在其抽象父类 AbstractJackson2HttpMessageConverter 和 AbstractGenericHttpMessageConverter 中实现
AbstractJackson2HttpMessageConverter canWrite 相关代码
public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> {@Overridepublic boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {// 此时 mediaType ==null 所以返回 trueif (!canWrite(mediaType)) {return false;}AtomicReference<Throwable> causeRef = new AtomicReference<>();if (this.objectMapper.canSerialize(clazz, causeRef)) {return true;}logWarningIfNecessary(clazz, causeRef.get());return false;}}
主要逻辑 canWrite
判断响应结果是否能够被该解析器解析 条件: 该类能够被序列化
AbstractGenericHttpMessageConverter write 相关代码
public abstract class AbstractGenericHttpMessageConverter<T> extends AbstractHttpMessageConverter<T> implements GenericHttpMessageConverter<T> {public final void write(final T t, @Nullable final Type type, @Nullable MediaType contentType,HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {final HttpHeaders headers = outputMessage.getHeaders();addDefaultHeaders(headers, t, contentType);if (outputMessage instanceof StreamingHttpOutputMessage) {StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;streamingOutputMessage.setBody(outputStream -> writeInternal(t, type, new HttpOutputMessage() {@Overridepublic OutputStream getBody() {return outputStream;}@Overridepublic HttpHeaders getHeaders() {return headers;}}));}else {// 调用的 jackson 原生的 write 方法writeInternal(t, type, outputMessage);outputMessage.getBody().flush();}}}
主要逻辑 write
最后同样调用的 Jackson 原生的 API。将结果写入到流中。
三、SpringBoot 更换 Json解析框架
以替换为 Gson 为例
方案一:剔除 Jackson 依赖
分析
在 Spring MVC 配置类 WebMvcConfigurationSupport 中的相关代码如下:
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {private static final boolean romePresent;private static final boolean jaxb2Present;private static final boolean jackson2Present;private static final boolean jackson2XmlPresent;private static final boolean jackson2SmilePresent;private static final boolean jackson2CborPresent;private static final boolean gsonPresent;private static final boolean jsonbPresent;static {ClassLoader classLoader = WebMvcConfigurationSupport.class.getClassLoader();romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader);}// 添加默认的 HttpMessageConverterprotected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {if (romePresent) {messageConverters.add(new AtomFeedHttpMessageConverter());messageConverters.add(new RssChannelHttpMessageConverter());}if (jackson2XmlPresent) {......}else if (jaxb2Present) {......}if (jackson2Present) {......}else if (gsonPresent) {......}else if (jsonbPresent) {......}if (jackson2SmilePresent) {......}if (jackson2CborPresent) {......}}}
在上面的分析中,Json 请求入参和json 响应结果最终都是有相关的 HttpMessageConverter 来解析的,而 HttpMessageConverter 匹配,在项目中,多个 HttpMessageConverter 是以 List 的方式存在的,也就是说,多个拥有相同解析功能的 HttpMessageConverter ,最终有谁来进行解析,取决于其在 List 中的先后顺序。况且在上述代码中能够知道,在存在有 jackson 相关依赖时,如:gson 之类的解析器并不为被注册。
执行操作
step1、移除 pom 中的关于 jackson 的依赖,例如下图:
step2、pom 中增加 gson 依赖,如下图:
方案二:
方案一,存在一个很大的问题,就是需要将依赖中所有的 jackson 相关的依赖提出,剔除不干净,同样会导致最终注册也是 Jackson 的解析器。
该方案直接通过 Spring MVC 自己手动配置,免除提出 jackson 依赖的繁琐工作
配置类如下:
/*** <p> web 配置类 </p>** @Author 彳失口亍*/@Configurationpublic class WebConfiguration implements WebMvcConfigurer {@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {// 剔除 MappingJackson2HttpMessageConverterconverters.removeIf(httpMessageConverter -> httpMessageConverter instanceof MappingJackson2HttpMessageConverter);// 手动添加 GsonHttpMessageConverterconverters.add(new GsonHttpMessageConverter());}}
直接剔除 jackson 解析器 MappingJackson2HttpMessageConverter ,然后添加自己需要的解析器即可。
