SpringBoot响应处理实现流程详解

目录

1、相关依赖

2、ReturnValueHandlers—返回值处理器

3、HttpMessageConvert—消息转换器

4、开启浏览器参数方式内容协商功能

1、相关依赖

web项目引入的启动器spring-boot-starter-web中含有

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId> <version>2.7.0</version> <scope>compile</scope> </dependency>

这个依赖下面又有jackson的相关依赖,用于json的转换

<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.3</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jdk8</artifactId> <version>2.13.3</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.13.3</version> <scope>compile</scope> </dependency> 2、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的返回值

3、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格式的转换

大概流程如下

判断当前响应头中是否已经有确定的媒体类型,即MediaType

获取客户端(PostMan、浏览器)支持接收的内容类型(获取客户端Accept请求头字段)

遍历循环所有当前系统的 MessageConverter,看谁支持操作这个对象(Person),找到支持操作Person的converter,把该converter支持的媒体类型统计出来放到集合中,这样就获取到服务端能提供哪些媒体类型

进行内容协商,找到最佳匹配媒体类型

用 支持 将对象转为 最佳匹配媒体类型 的converter,调用它进行转化

4、开启浏览器参数方式内容协商功能

如果需要返回xml格式的数据,那么需要额外导入相关依赖

<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency>

我们可以看到,浏览器是默认支持如下返回格式的,一般情况下,我们无法指定自己需要的返回格式

但是我们可以通过修改配置+添加参数的方式指定我们需要的格式

首先,我们在yaml文件中,开启基于浏览器参数方式内容协商功能

spring:
  mvc:
    contentnegotiation:
      favor-parameter: true

一旦此参数设置为true,那么我们的内容协商管理器contentNegotiationManager中,除了原有的从请求头获取媒体类型的策略之外,还多了一个从请求参数中获取媒体类型的策略,它支持xml和json两种媒体类型

然后我们使用http://localhost:8080/testPathVariable/001/split/decade?format=xml或者http://localhost:8080/testPathVariable/001/split/decade?format=json指定我们需要的返回格式

到此这篇关于SpringBoot响应处理实现流程详解的文章就介绍到这了,更多相关SpringBoot响应处理内容请搜索易知道(ezd.cc)以前的文章或继续浏览下面的相关文章希望大家以后多多支持易知道(ezd.cc)!

推荐阅读

    opporeno8参数配置及价格

    opporeno8参数配置及价格,面部,亿元,Oppo的荣誉2020年1月4日,接近屏幕关闭传感器是否支持双卡:支持oppor11splus什么时候上市的Oppo R11S P

    最新的AMD低端APUa46300盒装处理器上市

    最新的AMD低端APUa46300盒装处理器上市,,APU处理器,AMD的APU系列我们并不陌生,是目前流行的AMD主要处理器,一直很受用户的欢迎,在a10-5800k发

    高通骁龙695处理器排名第几

    高通骁龙695处理器排名第几,主频,麒麟,Red米6A屏幕在屏幕周围使用圆角设计,Red米6A加入了爱语音助手(小爱同学),安兔兔54W+麒麟990采用台积

    魅蓝note6性能参数有哪些

    魅蓝note6性能参数有哪些,摄像头,蓝牙,魅蓝note6性能参数有哪些魅力蓝色Note6最好拍照。电池寿命更长。蓝色Note6使用高通 snapdragon 625

    苹果笔记本 处理器 型号有哪些

    苹果笔记本 处理器 型号有哪些,处理器,苹果,笔记本,之前是不同的,最早是摩托罗拉的CPU,后来改IBM的CPU,最后才是一样的Intel,只有Intel的苹果

    哪个牌子的电脑处理器好?

    哪个牌子的电脑处理器好?,牌子,处理器,哪个,目前电脑处理器主要就是Intel和AMD这两个牌子,至于英特尔和AMD的处理器谁更好?这个还有看平台,服