首页 技术 正文
技术 2022年11月14日
0 收藏 885 点赞 4,373 浏览 4969 个字

问题描述

系统内配置了,ProtobufJsonFormatHttpMessageConverter和FastJsonHttpMessageConverter。

Spring官方内置的默认MessageConverter 比较标准,遇到什么 MediaType 就怎么解析。但是这两个比较特殊。

对于Protobuf生成的参数:

@PostMapping("/proto")
public ResponseEntity<String> proto(@RequestBody AddressBookProtos.Person person) {
try {
log.info("input is {}", JsonFormat.printer().print(person));
} catch (Exception e) {
//
}
return ResponseEntity.ok().body("ok");
}

这里用到的是普通的JSON请求,也就是Request Header 的 ContentType是 application/json;charset=UTF-8;

如果ProtobufJsonFormatHttpMessageConverter在FastJsonHttpMessageConverter 之后,那么读到的Protobuf消息是空白。

也就说:Controller的 RequestBody 参数是空白的字符串。

问题分析

先看 org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver 类:


//method: readWithMessageConverters()for (HttpMessageConverter<?> converter : this.messageConverters) {
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
GenericHttpMessageConverter<?> genericConverter =
(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
(targetClass != null && converter.canRead(targetClass, contentType))) {
if (message.hasBody()) {
HttpInputMessage msgToUse =
getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
}
else {
body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
}
break;
}
}

这个类说明,Spring会根据Convert列表,逐个调用converter.canRead,判断是否能够支持这种内容的读写。

FastJsonHttpMessageConverter 的canRead相当于直接返回true,因为mediaType 也支持 application/json;charset=UTF-8;

这里考虑到JSON只是一个字符串,所以没法根据类型判断能不能读。字符串肯定能读。所以FastJSON这个地方还不能直接说他这么设计不合理。

//FastJsonHttpMessageConverter.java    @Override
protected boolean supports(Class<?> clazz) {
return true;
} public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
return super.canRead(contextClass, mediaType);
}

所以如果先找到了FastJsonHttpMessageConverter,那么FastJSON不认识 protobuf的 Bean,无法进行读写,因此读到一个空字符串。

再看看ProtobufJsonFormatHttpMessageConverter的实现:

//org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter#supports@Override
protected boolean supports(Class<?> clazz) {
return Message.class.isAssignableFrom(clazz);
}

这里十分精确,他就是要支持Message接口的,所有的Protobuf定义message的时候,都会继承这个接口。

因此这里需要将 ProtobufJsonFormatHttpMessageConverter 提到FastJson之前。

解决方案

方案一


@Bean
public ProtobufJsonFormatHttpMessageConverter protobufJsonFormatHttpMessageConverter() {
return new ProtobufJsonFormatHttpMessageConverter();
}

这里定义的MessageConverter 会很早就扫描到Spring Context中。这里还不清楚为什么这个地方的ProtobufJsonFormatHttpMessageConverter 每次都是第一个。

尝试修改Configuration的类名字为z开头 也总是第一个。

同时FastJson转换器通常配置方式如下:

@Configuration
public class WebConfig implements WebMvcConfigurer { @Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteMapNullValue);
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
fastJsonHttpMessageConverter.setSupportedMediaTypes(Lists.newArrayList(MediaType.APPLICATION_JSON_UTF8));
converters.add(fastJsonHttpMessageConverter);
}
}

这样这个WebMvcConfigurer 在Spring Boot启动比较晚的时候才会加载,所以这里的MessageConverter 会排到最后面。

方案二(推荐)

@Configuration
public class WebConfig implements WebMvcConfigurer { @Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
ProtobufJsonFormatHttpMessageConverter protobufJsonFormatHttpMessageConverter = new ProtobufJsonFormatHttpMessageConverter();
converters.add(protobufJsonFormatHttpMessageConverter);
} @Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteMapNullValue);
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
fastJsonHttpMessageConverter.setSupportedMediaTypes(Lists.newArrayList(MediaType.APPLICATION_JSON_UTF8));
converters.add(fastJsonHttpMessageConverter);
}
}

这里configureMessageConverters 的调用顺序一定是在extendMessageConverters之前的。

参见:

//org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#getMessageConvertersprotected final List<HttpMessageConverter<?>> getMessageConverters() {
if (this.messageConverters == null) {
this.messageConverters = new ArrayList<>();
configureMessageConverters(this.messageConverters);
if (this.messageConverters.isEmpty()) {
addDefaultHttpMessageConverters(this.messageConverters);
}
extendMessageConverters(this.messageConverters);
}
return this.messageConverters;
}

Spring并没有对HttpMessageConverter做什么特殊的排序。(只针对XML的排到最后,"with some slight re-ordering to put XML converters at the back of the list")

另外参考一篇cnBlog文章 讲的HttpMessageConverter的比较详细。

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,031
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,520
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,368
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,148
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,781
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,860