一、知识回顾
前面,我们学习了,一个请求过来,先经过filter
组件,判断restful
风格接口的请求类型。
然后,通过HandlerMapping
找到处理该请求的接口。
接着,进入接口方法的参数解析环节,这里主要学习了参数解析器(argumentResolvers
)
和数据类型转换服务(Converters
),将请求的实参值与方法的形参绑定上。
然后,执行接口方法主体。
那么,方法体执行完后,就到了return
环节。就是返回值处理环节。
这一篇,我们就来看一下返回值处理的源码逻辑。
二、测试接口
接下来,我们就对@ResponseBody
标注的接口,进行返回值原理探究。
@ResponseBody //利用返回值处理器里面的消息转换器进行处理
@GetMapping(value = "/test/person")
public Person getPerson(){
Person person = new Person();
person.setAge(28);
person.setBirth(new Date());
person.setUserName("zhangsan");
return person;
}
我们知道,@ResponseBody
标注的接口,都会给页面返回json
数据。
三、源码解读
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
这里,我们看到了返回值处理器,默认是15个。
中间的其他步骤,我直接跳过了,进入关键代码
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite#handleReturnValue
这里,第一步,先获取能处理这个接口返回值类型的返回值处理器。这个获取逻辑,就是从15个返回值处理器中,循环遍历,找到具体的返回值处理器。
我们查看下返回值处理器接口规范
两个方法
supportsReturnType
:判断方法
handleReturnValue
:处理逻辑
会发现,这个设计模式和参数解析器一样思路。
断点放行下一步,得到@ResponseBody
标注的接口的返回值处理器是:
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#handleReturnValue
这个返回值处理方法,我们可以看到,最后一行的方法名
利用消息转换器写数据。
所以,返回值处理器,需要依赖底层的消息转换器
。
我们在看下消息转换器的具体逻辑
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor#writeWithMessageConverters(T, org.springframework.core.MethodParameter, org.springframework.http.server.ServletServerHttpRequest, org.springframework.http.server.ServletServerHttpResponse)
这里又出现一个新的组件,媒体类型
。
媒体类型:就是请求接口的客户端,如浏览器,postman
,传给接口的Accept
参数值。
目的就是告诉服务器接口,我能接收那些类型的返回值。
让springboot根据权重,选择最佳返回值返回给浏览器。
那么,这里就涉及到springboot
接口能生产那些类型的返回值。
然后,才能选择最优解。
这个选择过程,就叫内容协商
。
内容协商:说简单点,就是,浏览器和服务器商量,用什么样的结构数据返回给浏览器。
继续往下看
这里,我们获取到了,所有浏览器可以接收的返回值类型,和所有springboot
可以生产的返回值类型
接下来,就要利用嵌套for循环
确定出最终返回值类型。
经过循环处理后,得到14个可以返回的数据类型,并对这14个类型进行了权重排序。
m.m.a.RequestResponseBodyMethodProcessor : Using 'application/json;q=0.8', given [text/html, application/xhtml+xml, image/avif, image/webp, image/apng, application/xml;q=0.9, application/signed-exchange;v=b3;q=0.7, */*;q=0.8] and supported [application/json, application/*+json, application/json, application/*+json]
从日志看出,最终协商结果就是,返回json
数据格式。
内容协商阶段结束后,就要利用消息转换器(HttpMessageConverter)
,进行返回值数据格式化了。
把Java
对象,处理成json
结构,返回给浏览器。
springboot
默认的消息转换器有10中。
那么,哪个消息转换器负责处理json
返回值了?
这里,我们依然看一下消息转换器接口规范
有5个待实现的方法。
最终,是
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
来处理json
返回类型的数据。
调用对应的write
方法,写出json
数据给浏览器。
到这里,返回值处理的过程和主要组件介绍完毕。
四、逻辑梳理
简单来说就是以下几步:
首先,请求过来后,DispatcherServlet
中,会确定返回值处理器returnValueHandlers
。
默认有15种。
然后,具体的返回值处理器来进行处理。
返回值处理器中的逻辑如下
1、进行内容协商。
获取客户端发过来的能接收的所有媒体类型(MediaType
)。
评估自己能生产的所有数据类型(ProducibleMediaTypes
)
springboot
默认是从request
的Headers
中,获取Accept
参数值,作为媒体类型,进行内容协商。
其实,也可以从参数中获取。
后面给出案例。
2、找到具体的消息转换器(HttpMessageConverter
)。
springboot
默认配置了10种消息转换器。
将Java
对象write
给浏览器。