当前位置: 首页 > news >正文

【Spring Boot】响应JSON实现原理

1、ReturnValueHandlers—返回值处理器

之前我们分析了参数解析器argumentResolvers的相关源码,了解了请求中的参数是如何找到合适的参数解析器,并与方法的入参进行绑定的,那么问题来了,响应值的返回值处理器是如何找到的呢?

要分析源码,我们还是得进入DispatcherServlet这个类下的doDiapatch()方法
与之前分析参数解析器一样的流程,我们跟到了RequestMappingHandlerAdapter这个类下的invokeHandlerMethod()方法
通过断点可以看到我们默认支持15种返回值解析器
在这里插入图片描述

那么他是怎么从这么多返回值解析器中选出自己支持的那一个呢?
我们可以发现,这些返回值解析器都是实现了HandlerMethodReturnValueHandler接口

package org.springframework.web.method.support;

import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.web.context.request.NativeWebRequest;

public interface HandlerMethodReturnValueHandler {
    boolean supportsReturnType(MethodParameter returnType);

    void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}

此接口下只有2个方法,一个是判断是否支持此类型返回值,另一个是处理返回值的方法
是不是和之前的参数解析器的接口类有异曲同工之妙?
没错,我们正是使用这两个方法来寻找适合自己的返回值解析器以及处理我们的返回值

跟着断点一直向下,我们跟到了HandlerMethodReturnValueHandlerComposite类下的handleReturnValue()方法,在这里,我们就找到了对应的解析器并执行了相关方法

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
	// 获取自己适合的返回值解析器
    HandlerMethodReturnValueHandler handler = this.selectHandler(returnValue, returnType);
    if (handler == null) {
        throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
    } else {
    	// 调用该返回值解析器中的处理返回值的方法
        handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
    }
}

@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
	// 判断是否是异步的返回值
    boolean isAsyncValue = this.isAsyncReturnValue(value, returnType);
    Iterator var4 = this.returnValueHandlers.iterator();

    HandlerMethodReturnValueHandler handler;
    // 使用do-while进行遍历,找出支持当前返回值类型的解析器
    do {
        do {
            if (!var4.hasNext()) {
                return null;
            }

            handler = (HandlerMethodReturnValueHandler)var4.next();
        } while(isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler));
    } while(!handler.supportsReturnType(returnType));

    return handler;
}

最终,我们确定使用解析器RequestResponseBodyMethodProcessor可以处理标注了@ResponseBody的返回值

2、HttpMessageConvert—消息转换器

除了寻找合适的返回值解析器之外,我们还有一个问题要思考
为什么我们在控制器类的方法上加一个@ResponseBody注解就能将响应信息转换成json格式呢?
这个转换是怎样实现的呢?

我们接着上面的代码继续往下debug
之前我们已经定位到了使用RequestResponseBodyMethodProcessor来处理@ResponseBody的返回值,所以我们继续深入到这个类下的handleReturnValue()

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
	mavContainer.setRequestHandled(true);
	// 创建输入和输出信息
	ServletServerHttpRequest inputMessage = this.createInputMessage(webRequest);
	ServletServerHttpResponse outputMessage = this.createOutputMessage(webRequest);
	// 使用消息转换器进行写出操作
	this.writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

然后我们来分析解析器父类AbstractMessageConverterMethodProcessor下的writeWithMessageConverters()方法

首先我们了解一个概念:什么是内容协商?
浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型,这里的q代表优先级
然后服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据
在这里插入图片描述
所以在writeWithMessageConverters()方法中我们获取到浏览器能接受什么内容
在这里插入图片描述
以及服务器能产生什么内容
在这里插入图片描述
然后我们经过协商,决定返回application/json格式的数据
重点来了:这里有一系列的消息转换器,我们到底使用哪个呢?
在这里插入图片描述
随便点进一个消息转换器,我们发现他都是实现了HttpMessageConverter这个接口
这里面有2个方法,canRead()和canWrite()来帮助我们判断能否支持源类型和目标类型的消息读写
最终我们确定了,使用MappingJackson2HttpMessageConverter这个消息转换器来进行json转换

package org.springframework.http.converter;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;

public interface HttpMessageConverter<T> {
	// 读取,例如将json转为Person对象
    boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);

	// 写出,例如将Person转为json对象
    boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);

    List<MediaType> getSupportedMediaTypes();

    default List<MediaType> getSupportedMediaTypes(Class<?> clazz) {
        return !this.canRead(clazz, (MediaType)null) && !this.canWrite(clazz, (MediaType)null) ? Collections.emptyList() : this.getSupportedMediaTypes();
    }

    T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException;

    void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException;
}

然后我们在此处调用MappingJackson2HttpMessageConverter父类AbstractGenericHttpMessageConverter中的write()方法
在这里插入图片描述
在这里插入图片描述
走到AbstractJackson2HttpMessageConverter类下的writeInternal()方法
可以看出,它利用了底层的Jackson的objectMapper进行转换
这样,我们就完成了Person数据到json格式的转换

如有错误,欢迎指正!!!

相关文章:

  • 基于51单片机交通信号灯仿真_东西管制+南北管制
  • 2022“杭电杯”中国大学生算法设计超级联赛(4)
  • AngelScript -- C++程序最好的脚本语言
  • 如何编写整洁的代码
  • leetcode: 122. 买卖股票的最佳时机II
  • 字符串习题总结3
  • Java 操作RestHighLevelClient查询详解
  • 有效 TCP RST
  • 46.全排列 | 51.N皇后
  • 正则表达式的说明》
  • 【Vue】基础系列(三三)指令语法-事件及其修饰符,动态样式,v-model的用法,数据持久化存在本地localStorage,自定义指令
  • 3D感知技术(3)相机成像模型及相机标定
  • ThinkPHP 接口开发过程
  • Pytorch常用的4种随机数生成方法
  • java毕业设计闲一品交易平台mybatis+源码+调试部署+系统+数据库+lw
  • 分享的文章《人生如棋》
  • [译]前端离线指南(上)
  • angular组件开发
  • IE报vuex requires a Promise polyfill in this browser问题解决
  • js中的正则表达式入门
  • Linux各目录及每个目录的详细介绍
  • mysql innodb 索引使用指南
  • Odoo domain写法及运用
  • Spark VS Hadoop:两大大数据分析系统深度解读
  • win10下安装mysql5.7
  • 简单数学运算程序(不定期更新)
  • 简单易用的leetcode开发测试工具(npm)
  • 使用权重正则化较少模型过拟合
  • 双管齐下,VMware的容器新战略
  • 我从编程教室毕业
  • 一份游戏开发学习路线
  • 一个JAVA程序员成长之路分享
  • ​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、​​​​​​​数据同步psync)、总结
  • #Linux(Source Insight安装及工程建立)
  • $.ajax,axios,fetch三种ajax请求的区别
  • ( 用例图)定义了系统的功能需求,它是从系统的外部看系统功能,并不描述系统内部对功能的具体实现
  • (12)Linux 常见的三种进程状态
  • (仿QQ聊天消息列表加载)wp7 listbox 列表项逐一加载的一种实现方式,以及加入渐显动画...
  • (生成器)yield与(迭代器)generator
  • (使用vite搭建vue3项目(vite + vue3 + vue router + pinia + element plus))
  • (一)ClickHouse 中的 `MaterializedMySQL` 数据库引擎的使用方法、设置、特性和限制。
  • .NET CLR Hosting 简介
  • .NET 服务 ServiceController
  • .Net(C#)自定义WinForm控件之小结篇
  • .NET面试题(二)
  • @Autowired自动装配
  • [ C++ ] template 模板进阶 (特化,分离编译)
  • [16/N]论得趣
  • [C++]STL之map
  • [CQOI 2011]动态逆序对
  • [ES-5.6.12] x-pack ssl
  • [HTML]HTML5实现可编辑表格
  • [INSTALL_FAILED_TEST_ONLY],Android开发出现应用未安装
  • [ISITDTU 2019]EasyPHP
  • [MySQL]视图索引以及连接查询案列