当前位置:网站首页>八、响应处理——ReturnValueHandler匹配返回值处理器并处理返回值原理解析
八、响应处理——ReturnValueHandler匹配返回值处理器并处理返回值原理解析
2022-08-05 05:16:00 【呆比特】
响应处理——ReturnValueHandler匹配返回值处理器并处理返回值原理解析
前边已经分析了请求处理中从映射到获取参数值等底层原理,接下来的几篇将学习一下响应处理的相关过程
这一片学习Spring MVC如何快速返回一个JSON数据
首先我们的项目已经引入了 web 场景,web 场景中已经自动引入了 json 相关依赖
现在先写一个简单的controller来测试一下
//利用返回值处理器里面的消息转换器进行处理
@ResponseBody
@GetMapping(value = "/test/person")
public Person getPerson(){
Person person = new Person();
person.setAge(28);
person.setBirth(new Date());
person.setUserName("zhangsan");
return person;
}
接下来老套路,debug探索一下它的原理
直接跳过前边请求处理部分,来到真正处理请求的 RequestMappingHandlerAdapter.class 的 invokeHandlerMethod() 方法
在前边处理请求参数的时候,有一个参数解析器,同样,在处理返回值时,也有一个 返回值处理器
在方法返回之前,我们的返回值处理器已经准备好了,只等我们返回值时拿这些处理器来处理
下一步代码到 ServletInvocableHandlerMethod.class 的 invokeAndHandle 方法
以前我们是进到 invokeForRequest 方法看它怎么处理请求,现在就直接跳过往下走了
handleReturnValue 方法传入了我们的返回值对象,还有返回值类型,接下来,就在这个方法里边,利用对应的返回值处理器,帮我们处理返回值
我们前边的controller标注了 @ResponseBody , RequestResponseBodyMethodProcessor 处理器可以处理 @ResponseBody ,所以拿到了它
找到了处理器,接下来就看它是怎样处理的
step into 进入 RequestResponseBodyMethodProcessor.class 的 handleReturnValue 方法
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
mavContainer.setRequestHandled(true);
//包装原生request、response
ServletServerHttpRequest inputMessage = this.createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = this.createOutputMessage(webRequest);
//传入返回值,返回值类型,以及请求响应,使用消息转换器进行写出操作
this.writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
进入 writeWithMessageConverters 方法,下边要进行的操作叫做 内容协商 ,浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型,服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据
//省略前边一些判断代码,直接来到内容协商
MediaType selectedMediaType = null;
//先看响应头中有没有内容类型
MediaType contentType = outputMessage.getHeaders().getContentType();
boolean isContentTypePreset = contentType != null && contentType.isConcrete();
if (isContentTypePreset) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Found 'Content-Type:" + contentType + "' in response");
}
//如果已经有了,就直接用
selectedMediaType = contentType;
} else {
//如果没有,就拿到原生 request 对象,
HttpServletRequest request = inputMessage.getServletRequest();
//获取能接受的内容类型,就是响应头中Accept的内容
List<MediaType> acceptableTypes = this.getAcceptableMediaTypes(request);
//最终能生成的内容类型
List<MediaType> producibleTypes = this.getProducibleMediaTypes(request, valueType, (Type)targetType);
if (body != null && producibleTypes.isEmpty()) {
throw new HttpMessageNotWritableException("No converter found for return value of type: " + valueType);
}
List<MediaType> mediaTypesToUse = new ArrayList();
Iterator var15 = acceptableTypes.iterator();
//服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据
MediaType mediaType;
while(var15.hasNext()) {
mediaType = (MediaType)var15.next();
Iterator var17 = producibleTypes.iterator();
while(var17.hasNext()) {
MediaType producibleType = (MediaType)var17.next();
if (mediaType.isCompatibleWith(producibleType)) {
mediaTypesToUse.add(this.getMostSpecificMediaType(mediaType, producibleType));
}
}
}
if (mediaTypesToUse.isEmpty()) {
if (body != null) {
throw new HttpMediaTypeNotAcceptableException(producibleTypes);
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
}
return;
}
MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
var15 = mediaTypesToUse.iterator();
while(var15.hasNext()) {
mediaType = (MediaType)var15.next();
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
}
经过上边的代码,就拿到了内容类型
拿到了内容类型之后,SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ,看谁能处理?
循环判断默认的MessageConverter看哪个能干活
找到了 MappingJackson2HttpMessageConverter ,它的supports方法直接返回true,啥都支持
最终,利用MappingJackson2HttpMessageConverter将对象转为json再写出去
总结
1. 返回值处理器判断是否支持这种类型返回值 supportsReturnType
2. 返回值处理器调用 handleReturnValue 进行处理
3. RequestResponseBodyMethodProcessor 可以处理返回值标了@ResponseBody 注解的。
利用 MessageConverters 进行处理 将数据写为json
①内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型)
②服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据,
③SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ,看谁能处理?
得到MappingJackson2HttpMessageConverter可以将对象写为json
利用MappingJackson2HttpMessageConverter将对象转为json再写出去。
OVER(∩_∩)O~
边栏推荐
- 面向小白的深度学习代码库,一行代码实现30+中attention机制。
- 盘点关于发顶会顶刊论文,你需要知道写作上的这些事情!
- 【论文精读】R-CNN 之预测框回归(Bounding box regression)问题详述
- 吞吐?带宽?傻傻分不清楚
- IT系统运行维护方法及策略
- You should write like this
- spark-DataFrame数据插入mysql性能优化
- flink项目开发-flink的scala shell命令行交互模式开发
- Flink Broadcast 广播变量
- 【Pytorch学习笔记】9.分类器的分类结果如何评估——使用混淆矩阵、F1-score、ROC曲线、PR曲线等(以Softmax二分类为例)
猜你喜欢
OSPF网络类型
华科提出首个用于伪装实例分割的一阶段框架OSFormer
SQL(1) - Add, delete, modify and search
[Database and SQL study notes] 8. Views in SQL
伪RTOS-ProroThread在CH573芯片上的移植
基于STM32F407的WIFI通信(使用的是ESP8266模块)
【论文精读】ROC和PR曲线的关系(The relationship between Precision-Recall and ROC curves)
CVPR2021 - Inception Convolution with Efficient Dilation Search
[Intensive reading of the paper] R-CNN's Bounding box regression problem is detailed
The difference between the operators and logical operators
随机推荐
[After a 12] No record for a whole week
【数据库和SQL学习笔记】4.SELECT查询2:排序(ORDER BY)、聚合函数、分组查询(GROUP BY)
spingboot 容器项目完成CICD部署
BFC详解(Block Formmating Context)
MySQL
【论文阅读-表情捕捉】ExpNet: Landmark-Free, Deep, 3D Facial Expressions
CVPR2020 - 自校准卷积
RecycleView和ViewPager2
flink项目开发-flink的scala shell命令行交互模式开发
华科提出首个用于伪装实例分割的一阶段框架OSFormer
Kubernetes常备技能
用GAN的方法来进行图片匹配!休斯顿大学提出用于文本图像匹配的对抗表示学习,消除模态差异!
CVPR best paper winner Huang Gao's team from Tsinghua University presented the first dynamic network review
【22李宏毅机器学习】课程大纲概述
MSRA proposes extreme masking model ExtreMA for learning instances and distributed visual representations
原来何恺明提出的MAE还是一种数据增强
《基于机器视觉的输电线路交叉点在线测量方法及技术方案》论文笔记
伪RTOS-ProroThread在CH573芯片上的移植
解决:Unknown column ‘id‘ in ‘where clause‘ 问题
【Pytorch学习笔记】11.取Dataset的子集、给Dataset打乱顺序的方法(使用Subset、random_split)