14.HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler
14.HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler
Spring 版本:5.1.14.RELEASE
该系列其他文档请查看:《死磕 Spring MVC 源码分析 - 文章导读》
HandlerAdapter 组件
HandlerAdapter 组件,处理器的适配器。因为处理器 handler
的类型是 Object 类型,需要有一个调用者来实现 handler
是怎么被执行。Spring 中的处理器的实现多变,比如用户的处理器可以实现 Controller 接口或者 HttpRequestHandler 接口,也可以用 @RequestMapping
注解将方法作为一个处理器等,这就导致 Spring MVC 无法直接执行这个处理器。所以这里需要一个处理器适配器,由它去执行处理器
由于HandlerMapping 组件涉及到的内容较多,考虑到内容的排版,所以将这部分内容拆分成了五个模块,依次进行分析:
- 《HandlerAdapter 组件(一)之 HandlerAdapter》
- 《HandlerAdapter 组件(二)之 ServletInvocableHandlerMethod》
- 《HandlerAdapter 组件(三)之 HandlerMethodArgumentResolver》
- 《HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler》
- 《HandlerAdapter 组件(五)之 HttpMessageConverter》
HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler
本文是接着《HandlerAdapter 组件(三)之 HandlerMethodArgumentResolver》一文来分享 HandlerMethodReturnValueHandler 组件。在 HandlerAdapter
执行处理器的过程中,具体的执行过程交由 ServletInvocableHandlerMethod
对象来完成,其中需要先通过 HandlerMethodArgumentResolver
参数解析器从请求中解析出方法的入参,然后再通过反射机制调用对应的方法,获取到执行结果后需要通过 HandlerMethodReturnValueHandler 结果处理器来进行处理。
点击这里:商城系统实战、秒杀系统实战、代码生成工具实战、工作经验分享、技术选型、系统设计、性能优化、源码解读、高频面试题,这里什么都有
回顾
先来回顾一下 ServletInvocableHandlerMethod
在哪里调用返回值处理器的,可以回到 《HandlerAdapter 组件(二)之 ServletInvocableHandlerMethod》 中 ServletInvocableHandlerMethod 小节下面的 invokeAndHandle
方法,如下:
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// <1> 执行调用
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// <2> 设置响应状态码
setResponseStatus(webRequest);
// <3> 设置 ModelAndViewContainer 为请求已处理,返回,和 @ResponseStatus 注解相关
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
} else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
// <4> 设置 ModelAndViewContainer 为请求未处理
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// <5> 处理返回值
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
} catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
<5>
处调用 returnValueHandlers 对返回结果进行处理- returnValueHandlers 为 HandlerMethodReturnValueHandlerComposite 组合对象,包含了许多的结果处理器
HandlerMethodReturnValueHandler 接口
org.springframework.web.method.support.HandlerMethodReturnValueHandler
,返回结果处理器
public interface HandlerMethodReturnValueHandler {
/**
* 是否支持该类型
*/
boolean supportsReturnType(MethodParameter returnType);
/**
* 处理返回值
*/
void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
类图

因为返回结果类型是多变的,所以会有许多的 HandlerMethodReturnValueHandler 的实现类,上图仅列出了本文会分析的两个实现类
ModelAndViewContainer
org.springframework.web.method.support.ModelAndViewContainer
,主要是作为 Model 和 View 的容器
构造方法
public class ModelAndViewContainer {
/**
* 是否在 redirect 重定向时,忽略 {@link #redirectModel}
*/
private boolean ignoreDefaultModelOnRedirect = false;
/**
* 视图,Object 类型。
*
* 实际情况下,也可以是 String 类型的逻辑视图
*/
@Nullable
private Object view;
/**
* 默认使用的 Model 。实际上是个 Map
*/
private final ModelMap defaultModel = new BindingAwareModelMap();
/**
* redirect 重定向的 Model ,在重定向时使用。
*/
@Nullable
private ModelMap redirectModel;
/**
* 处理器返回 redirect 视图的标识
*/
private boolean redirectModelScenario = false;
/**
* Http 响应状态
*/
@Nullable
private HttpStatus status;
private final Set<String> noBinding = new HashSet<>(4);
private final Set<String> bindingDisabled = new HashSet<>(4);
/**
* 用于设置 SessionAttribute 的标识
*/
private final SessionStatus sessionStatus = new SimpleSessionStatus();
/**
* 请求是否处理完的标识
*/
private boolean requestHandled = false;
}
getModel
getModel()
方法,获得 Model 对象。代码如下:
public ModelMap getModel() {
// 是否使用默认 Model
if (useDefaultModel()) {
return this.defaultModel;
}
else {
if (this.redirectModel == null) {
this.redirectModel = new ModelMap();
}
return this.redirectModel;
}
}
/**
* Whether to use the default model or the redirect model.
*/
private boolean useDefaultModel() {
return (!this.redirectModelScenario || (this.redirectModel == null && !this.ignoreDefaultModelOnRedirect));
}
从代码中,可以看出,有两种情况下,使用 defaultModel 默认 Model :
情况一
!this.redirectModelScenario
,处理器返回 redirect 视图的标识为false
的时候,即不重定向情况二
this.redirectModel == null && !this.ignoreDefaultModelOnRedirect
,redirectModel
重定向 Model 为空,并且ignoreDefaultModelOnRedirect
为true
,即忽略defaultModel
那么,问题就来了,redirectModelScenario 和 ignoreDefaultModelOnRedirect 什么时候被改变?
redirectModelScenario
属性,在下文的 ViewNameMethodReturnValueHandler的handleReturnValue方法中会设置为true
,详情见下文ignoreDefaultModelOnRedirect
属性,和 RequestMappingHandlerAdapter 的ignoreDefaultModelOnRedirect
的属性是一致的,默认为false
在 RequestMappingHandlerAdapter 的
invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod)
方法中,进行设置另外,org.springframework.ui.ModelMap 是继承 LinkedHashMap 类,并增加了部分常用方法,比较简单
View 相关的方法
public void setViewName(@Nullable String viewName) {
this.view = viewName;
}
@Nullable
public String getViewName() {
return (this.view instanceof String ? (String) this.view : null);
}
public void setView(@Nullable Object view) {
this.view = view;
}
@Nullable
public Object getView() {
return this.view;
}
public boolean isViewReference() {
return (this.view instanceof String);
}
requestHandled 属性
请求是否处理完的标识
关于requestHandled
的修改地方,实际在 Spring MVC 地方蛮多处都可以进行修改,例如:
- 在本文的开始处,ServletInvocableHandlerMethod 对象的 invokeAndHandle 方法中,会先设置为 false,表示请求还未处理,再交由 HandlerMethodReturnValueHandler 结果处理器去处理
- 在后文的 RequestResponseBodyMethodProcessor 的 handleReturnValue 会设置为 true
处理完结果后,接下来 RequestMappingHandlerAdapter
需要通过 ModelAndViewContainer
获取 ModelAndView
对象,会用到 requestHandled
这个属性
// RequestMappingHandlerAdapter.java
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer);
// 情况一,如果 mavContainer 已处理,则返回“空”的 ModelAndView 对象。
if (mavContainer.isRequestHandled()) {
return null;
}
// 情况二,如果 mavContainer 未处理,则基于 mavContainer 生成 ModelAndView 对象
ModelMap model = mavContainer.getModel();
// 创建 ModelAndView 对象,并设置相关属性
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}
看到没,如果已处理,则返回的 ModelAndView
对象为 null
HandlerMethodReturnValueHandlerComposite
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite
,实现 HandlerMethodReturnValueHandler 接口,复合的 HandlerMethodReturnValueHandler 实现类
构造方法
public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {
/** HandlerMethodReturnValueHandler 数组 */
private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();
}
在《HandlerAdapter 组件(一)之 HandlerAdapter》的RequestMappingHandlerAdapter小节的 getDefaultReturnValueHandlers 方法中可以看到,默认的 returnValueHandlers 有哪些 HandlerMethodReturnValueHandler 实现类,注意这里是有顺序的添加哦
getReturnValueHandler
getReturnValueHandler(MethodParameter returnType)
方法,获得方法返回值对应的 HandlerMethodReturnValueHandler 对象,方法如下:
@Nullable
private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
很简单,遍历所有的 HandlerMethodReturnValueHandler 实现类,如果支持这个返回结果,则直接返回
这里为什么不加缓存呢?
supportsReturnType
supportsReturnType(MethodParameter returnType)
方法,判断是否支持该返回类型,方法如下:
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return getReturnValueHandler(returnType) != null;
}
实际上就是调用 getReturnValueHandler(MethodParameter returnType)
方法,存在对应的 HandlerMethodReturnValueHandler 实现类表示支持
handleReturnValue
handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
方法,处理返回值,方法如下:
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// <x> 获得 HandlerMethodReturnValueHandler 对象
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
这里好奇的是没有调用 getReturnValueHandler(MethodParameter returnType)
方法获取对应的 HandlerMethodReturnValueHandler 对象,而是调用 selectHandler(Object value, MethodParameter returnType)
方法,方法如下:
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
// 判断是否为异步返回值
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
// 遍历 HandlerMethodReturnValueHandler 数组,逐个判断是否支持
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
// 如果支持,则返回
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
private boolean isAsyncReturnValue(@Nullable Object value, MethodParameter returnType) {
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (handler instanceof AsyncHandlerMethodReturnValueHandler &&
((AsyncHandlerMethodReturnValueHandler) handler).isAsyncReturnValue(value, returnType)) {
return true;
}
}
return false;
}
在getReturnValueHandler(MethodParameter returnType)
的基础上,增加了异步处理器 AsyncHandlerMethodReturnValueHandler 的判断
【重点】RequestResponseBodyMethodProcessor
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
,继承 AbstractMessageConverterMethodProcessor 抽象类,处理方法参数添加了 @RequestBody
注解方法入参,或者处理方法添加了 @ResponseBody
注解的返回值。
因为前后端分离之后,后端基本是提供 Restful API ,所以 RequestResponseBodyMethodProcessor 成为了目前最常用的 HandlerMethodReturnValueHandler 实现类。

从图中,我们也会发现,RequestResponseBodyMethodProcessor 也是 HandlerMethodArgumentResolver 的实现类。示例代码:
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/walks")
public List<User> walk(@RequestBody User user) {
List<User> users = new ArrayList();
users.add(new User().setUsername("nihao"));
users.add(new User().setUsername("zaijian"));
return users;
}
}
虽然,walks()
方法的返回值没添加 @ResponseBody
注解,但是 @RestController
注解,默认有 @ResponseBody
注解
构造方法
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters) {
super(converters);
}
public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters,
@Nullable ContentNegotiationManager manager) {
super(converters, manager);
}
public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters,
@Nullable List<Object> requestResponseBodyAdvice) {
super(converters, null, requestResponseBodyAdvice);
}
public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters,
@Nullable ContentNegotiationManager manager, @Nullable List<Object> requestResponseBodyAdvice) {
super(converters, manager, requestResponseBodyAdvice);
}
}
- converters 参数,HttpMessageConverter 数组。关于 HttpMessageConverter,就是将返回结果设置到响应中,供客户端获取。例如,我们想要将 POJO 对象,返回成 JSON 数据给前端,就会使用到 MappingJackson2HttpMessageConverter 类。
- requestResponseBodyAdvice 参数,在父类 AbstractMessageConverterMethodArgumentResolver 中会将其转换成 RequestResponseBodyAdviceChain 对象 advice,不知你是否还记得这个参数,来回顾一下:
在《HandlerAdapter 组件(一)之 HandlerAdapter》的RequestMappingHandlerAdapter的1.afterPropertiesSet 初始化方法中,第一步就会初始化所有 ControllerAdvice 相关的类
然后在1.4 getDefaultReturnValueHandlers方法中,创建 RequestResponseBodyMethodProcessor 处理器时,会传入 requestResponseBodyAdvice
参数
使用示例可以参考 SpringMVC 中 @ControllerAdvice 注解的三种使用场景
supportsParameter
实现supportsParameter(MethodParameter returnType)
方法,判断是否支持处理该方法参数,方法如下:
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 该参数是否有 @RequestBody 注解
return parameter.hasParameterAnnotation(RequestBody.class);
}
该方法参数是否有 @RequestBody
注解
resolveArgument
实现resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory)
方法,从请求中解析出带有 @RequestBody
注解的参数,方法如下:
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
// 从请求体中解析出方法入参对象
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);
// 数据绑定相关
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}
// 返回方法入参对象,如果有必要,则通过 Optional 获取对应的方法入参
return adaptArgumentIfNecessary(arg, parameter);
}
调用readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter, Type paramType)
方法,从请求体中解析出方法入参对象
【核心】readWithMessageConverters
从请求体中解析出方法入参,方法如下:
@Override
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
// <1> 创建 ServletServerHttpRequest 请求对象
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
Assert.state(servletRequest != null, "No HttpServletRequest");
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
// <2> 读取请求体中的消息并转换成入参对象
Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
// <3> 校验方法入参对象
if (arg == null && checkRequired(parameter)) {
throw new HttpMessageNotReadableException("Required request body is missing: " +
parameter.getExecutable().toGenericString(), inputMessage);
}
return arg;
}
// AbstractMessageConverterMethodArgumentResolver.java
@Nullable
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
// <1> 获取使用的 MediaType 对象
MediaType contentType;
boolean noContentType = false;
try {
// <1.1> 从请求头中获取 "Content-Type"
contentType = inputMessage.getHeaders().getContentType();
}
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
}
if (contentType == null) {
noContentType = true;
// <1.2> 为空则默认为 application/octet-stream
contentType = MediaType.APPLICATION_OCTET_STREAM;
}
// <2> 获取方法参数的 containing class 和 目标类型,用于 HttpMessageConverter 解析
Class<?> contextClass = parameter.getContainingClass();
Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
if (targetClass == null) {
// 如果为空,则从方法参数中解析出来
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
targetClass = (Class<T>) resolvableType.resolve();
}
// <3> 获取 HTTP 方法
HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
Object body = NO_VALUE;
// <4> 开始从请求中解析方法入参
EmptyBodyCheckingHttpInputMessage message;
try {
// `<4.1>` 将请求消息对象封装成 EmptyBodyCheckingHttpInputMessage,用于校验是否有请求体,没有的话设置为 null
message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
// <4.2> 遍历 HttpMessageConverter
for (HttpMessageConverter<?> converter : this.messageConverters) {
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
GenericHttpMessageConverter<?> genericConverter = (converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
// 如果该 HttpMessageConverter 能够读取当前请求体解析出方法入参
if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
(targetClass != null && converter.canRead(targetClass, contentType))) {
// <4.2.1> 如果请求体不为空
if (message.hasBody()) {
HttpInputMessage msgToUse = getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
// 通过该 HttpMessageConverter 从请求体中解析出方法入参对象
body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
// 调用 RequestResponseBodyAdvice 的 afterBodyRead 方法,存在 RequestBodyAdvice 则对方法入参进行修改
body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
}
// <4.2.2> 如果请求体为空,则无需解析请求体
else {
// 调用 RequestResponseBodyAdvice 的 afterBodyRead 方法,存在 RequestBodyAdvice 则对方法入参进行修改
body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
}
break;
}
}
}
catch (IOException ex) {
throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
}
// <5> 校验解析出来的方法入参对象是否为空
if (body == NO_VALUE) {
if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
(noContentType && !message.hasBody())) {
return null;
}
throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
}
// 打印日志
MediaType selectedContentType = contentType;
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(theBody, !traceOn);
return "Read \"" + selectedContentType + "\" to [" + formatted + "]";
});
// <6> 返回方法入参对象
return body;
}
我们直接看到父类 AbstractMessageConverterMethodArgumentResolver 的 readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,Type targetType)
这个核心方法,大致逻辑如下:
1、 获取使用的MediaType对象contentType
;
1、 从请求头中获取Content-Type
;
2、 请求头中没有则设置为默认的类型application/octet-stream
;
2、 获取方法参数的containingclass
和targetClass目标类型
,用于HttpMessageConverter解析;
3、 获取HTTP方法;
4、 开始从请求中解析方法入参Objectbody
;
1、 将请求消息对象封装成EmptyBodyCheckingHttpInputMessage,用于校验是否有请求体,没有的话设置为null
;
2、 遍历所有的HttpMessageConverter实现类,调用其canRead(Typetype,@NullableClass<?>contextClass,@NullableMediaTypemediaType)
方法,判断当前HttpMessageConverter实现类是否支持解析该方法入参,如果返回true
,则使用该HttpMessageConverter实现类进行解析;
1. 如果请求体不为空,则通过该 HttpMessageConverter 从请求体中解析出方法入参对象
2. 如果请求体为空,则无需解析请求体
注意:上面不管请求体是否为空,都会调用 `RequestResponseBodyAdvice` 的 `afterBodyRead` 方法,存在 RequestBodyAdvice 则对方法入参进行修改
5、 校验解析出来的方法入参对象是否为空,抛出异常或者返回null
;
6、 返回方法入参对象body
;
方法虽然很长,但是不难理解,大致逻辑就是找到合适的 HttpMessageConverter 实现类从请求体中获取到方法入参对象
逻辑和下面的 writeWithMessageConverters
差不多,我们重点来看到下面这个方法😈
supportsReturnType
实现supportsReturnType(MethodParameter returnType)
方法,判断是否支持处理该返回类型,方法如下:
@Override
public boolean supportsReturnType(MethodParameter returnType) {
// 该方法或者所在类是否有 @ResponseBody 注解
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
该方法或者所在类是否有 @ResponseBody
注解
handleReturnValue
实现handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
方法,方法如下:
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
// <1> 设置已处理
mavContainer.setRequestHandled(true);
// <2> 创建请求和响应
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
// <3> 使用 HttpMessageConverter 对对象进行转换,并写入到响应
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
1、 设置mavContainer
已处理,也就是修改它的requestHandled
属性为true
,表示请求已处理,后续获取到的ModelAndView
对象就为null
;
2、 创建请求和响应,这里是获取到javax.servlet.http.HttpServletRequest
和javax.servlet.http.HttpServletResponse
,然后分别封装成org.springframework.http.server.ServletServerHttpRequest
和org.springframework.http.server.ServletServerHttpResponse
对象,便于从请求中获取数据,往响应中设置数据;
3、 调用父类AbstractMessageConverterMethodProcessor的writeWithMessageConverters(returnValue,returnType,inputMessage,outputMessage)
方法,使用HttpMessageConverter对对象进行转换,并写入到响应;
不知你是否还记得 HttpMessageConverter 是在哪儿会初始化呢?我们来回顾一下
回到《HandlerAdapter 组件(一)之 HandlerAdapter》的RequestMappingHandlerAdapter的构造方法中,默认会添加了四个 HttpMessageConverter 对象。当然,默认还会添加其他的,例如 MappingJackson2HttpMessageConverter 为 JSON 消息格式的转换器,至于其他 HttpMessageConverter 实现类如何添加的,本文就不分析了,你知道就行😈
然后在 1.4 getDefaultReturnValueHandlers 方法中,创建 RequestResponseBodyMethodProcessor 处理器时,会传入
getMessageConverters()
参数,也就是获取所有的 HttpMessageConverter 实现类,所以在下面这个方法就需要用到
【核心】writeWithMessageConverters
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage)
方法,使用 HttpMessageConverter 对象进行转换,并写入到响应,方法如下:
// AbstractMessageConverterMethodProcessor.java
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
// <1> 获得 body、valueType、targetType
Object body;
Class<?> valueType;
Type targetType;
if (value instanceof CharSequence) { // 如果是字符串则直接赋值
body = value.toString();
valueType = String.class;
targetType = String.class;
}
else {
body = value;
// 获取返回结果的类型(返回值 body 不为空则直接获取其类型,否则从返回结果类型 returnType 获取其返回值类型)
valueType = getReturnValueType(body, returnType);
// 获取泛型
targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
}
// <2> 是否为 Resource 类型
if (isResourceType(value, returnType)) {
// 设置响应头 Accept-Ranges
outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
// 数据不为空、请求头中的 Range 不为空、响应码为 200
if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null
&& outputMessage.getServletResponse().getStatus() == 200) {
Resource resource = (Resource) value;
try {
List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
// 断点续传,客户端已下载一部分数据,此时需要设置响应码为 206
outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
// 获取哪一段数据需返回
body = HttpRange.toResourceRegions(httpRanges, resource);
valueType = body.getClass();
targetType = RESOURCE_REGION_LIST_TYPE;
}
catch (IllegalArgumentException ex) {
outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
}
}
}
// <3> 选择使用的 MediaType
MediaType selectedMediaType = null;
// <3.1> 获得响应中的 ContentType 的值
MediaType contentType = outputMessage.getHeaders().getContentType();
// <3.1.1> 如果存在 ContentType 的值,并且不包含通配符,则使用它作为 selectedMediaType
if (contentType != null && contentType.isConcrete()) {
if (logger.isDebugEnabled()) {
logger.debug("Found 'Content-Type:" + contentType + "' in response");
}
selectedMediaType = contentType;
}
else {
HttpServletRequest request = inputMessage.getServletRequest();
// <3.2.1> 从请求中,获得可接受的 MediaType 数组。默认实现是,从请求头 ACCEPT 中获取
List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
// <3.2.2> 获得可产生的 MediaType 数组
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
// <3.2.3> 如果 body 非空,并且无可产生的 MediaType 数组,则抛出 HttpMediaTypeNotAcceptableException 异常
if (body != null && producibleTypes.isEmpty()) {
throw new HttpMessageNotWritableException(
"No converter found for return value of type: " + valueType);
}
// <3.2.4> 通过 acceptableTypes 来比对,将符合的 producibleType 添加到 mediaTypesToUse 结果数组中
List<MediaType> mediaTypesToUse = new ArrayList<>();
for (MediaType requestedType : acceptableTypes) {
for (MediaType producibleType : producibleTypes) {
if (requestedType.isCompatibleWith(producibleType)) {
mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
}
}
}
// <3.2.5> 如果没有符合的,并且 body 非空,则抛出 HttpMediaTypeNotAcceptableException 异常
if (mediaTypesToUse.isEmpty()) {
if (body != null) {
throw new HttpMediaTypeNotAcceptableException(producibleTypes);
}
if (logger.isDebugEnabled()) {
logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
}
return;
}
// <3.2.6> 按照 MediaType 的 specificity 和 quality 排序
MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
// <3.2.7> 选择其中一个最匹配的,主要考虑不包含通配符的,例如 application/json;q=0.8
for (MediaType mediaType : mediaTypesToUse) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
}
else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}
if (logger.isDebugEnabled()) {
logger.debug("Using '" + selectedMediaType + "', given " +
acceptableTypes + " and supported " + producibleTypes);
}
}
// <4> 如果匹配到,则进行写入逻辑
if (selectedMediaType != null) {
// <4.1> 移除 quality 。例如,application/json;q=0.8 移除后为 application/json
selectedMediaType = selectedMediaType.removeQualityValue();
// <4.2> 遍历 messageConverters 数组
for (HttpMessageConverter<?> converter : this.messageConverters) {
// <4.3> 判断 HttpMessageConverter 是否支持转换目标类型
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter
? (GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType)
: converter.canWrite(valueType, selectedMediaType)) {
// <5.1> 如果有 RequestResponseBodyAdvice,则可能需要对返回的结果做修改
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
// <5.2> body 非空,则进行写入
if (body != null) {
// 打印日志
Object theBody = body; // 这个变量的用途是,打印是匿名类,需要有 final
LogFormatUtils.traceDebug(logger, traceOn -> "Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
// 添加 CONTENT_DISPOSITION 头,一般情况下用不到
addContentDispositionHeader(inputMessage, outputMessage);
// <5.3> 写入内容
if (genericConverter != null) {
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
}
else {
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Nothing to write: null body");
}
}
// <5.4> return 返回,结束整个逻辑
return;
}
}
}
// <6> 如果到达此处,并且 body 非空,说明没有匹配的 HttpMessageConverter 转换器,则抛出 HttpMediaTypeNotAcceptableException 异常
if (body != null) {
throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}
}
方法有点长,慢慢来看,核心逻辑简单😈
<1>
处,获得 body
、valueType
、targetType
三个属性,例如上面提供的示例,三个值分对应users返回结果
、ArrayList 类型
、User 类型
<2>
处,调用 isResourceType(Object value, MethodParameter returnType)
方法,判断是否为 Resource 类型,方法如下:
// AbstractMessageConverterMethodProcessor.java
protected boolean isResourceType(@Nullable Object value, MethodParameter returnType) {
Class<?> clazz = getReturnValueType(value, returnType);
return clazz != InputStreamResource.class && Resource.class.isAssignableFrom(clazz);
}
设置响应头 Accept-Ranges 为 "bytes",如果数据不为空,且请求头中的 Range 不为空,且响应码为 200,则设置状态码为 206(断点续传,客户端已下载一部分数据),这里不做过多的讲述
========== 第一步 ==========
1、 选择使用的MediaType对象selectedMediaType
;
1、 获得响应中的ContentType的值,如果存在ContentType的值,并且不包含通配符,则使用它作为selectedMediaType
;
2、 否则,从请求中找到合适的MediaType对象;
1. 从请求中,获得可接受的 MediaType 数组 `acceptableTypes`。默认实现是,从请求头 ACCEPT 中获取
2. 获得可产生的 MediaType 数组 `producibleTypes`
3. 如果 `body` 非空,并且无可产生的 MediaType 数组 `producibleTypes`,则抛出 HttpMediaTypeNotAcceptableException 异常
4. 通过 `acceptableTypes` 来比对,将符合的 `producibleType` 添加到 `mediaTypesToUse` 结果数组中
5. 如果没有符合的,并且 `body` 非空,则抛出 HttpMediaTypeNotAcceptableException 异常
6. 按照 MediaType 的 specificity 和 quality 排序(权重),对`mediaTypesToUse` 进行排序
7. 选择其中一个最匹配的,主要考虑不包含通配符的,例如 `application/json;q=0.8`
========== 第二步 ==========
1、 如果匹配到MediaType对象selectedMediaType
不为空,则进行写入逻辑;
1、 移除quality例如,application/json;q=0.8
移除后为application/json
;
2、 遍历messageConverters
数组,也就是所有的HttpMessageConverter实现类;
3、 判断当前HttpMessageConverter是否支持转换目标类型,调用其canWrite(@NullableTypetype,Class<?>clazz,@NullableMediaTypemediaType)
方法进行判断;
========== 第三步:写入响应体==========
1、 如果4.3
的结果为true
,表示当前HttpMessageConverter实现类可以处理该返回类型;
1、 调用RequestResponseBodyAdvice
的beforeBodyWrite
方法,存在ResponseBodyAdvice则对返回的结果进行修改;
```java
// RequestResponseBodyAdviceChain.java
@Override
@Nullable
public Object beforeBodyWrite(@Nullable Object body, MethodParameter returnType, MediaType contentType,
Class<? extends HttpMessageConverter<?>> converterType,
ServerHttpRequest request, ServerHttpResponse response) {
return processBody(body, returnType, contentType, converterType, request, response);
}
@Nullable
private <T> Object processBody(@Nullable Object body, MethodParameter returnType, MediaType contentType,
Class<? extends HttpMessageConverter<?>> converterType,
ServerHttpRequest request, ServerHttpResponse response) {
for (ResponseBodyAdvice<?> advice : getMatchingAdvice(returnType, ResponseBodyAdvice.class)) {
if (advice.supports(returnType, converterType)) {
body = ((ResponseBodyAdvice<T>) advice).beforeBodyWrite((T) body, returnType,
contentType, converterType, request, response);
}
}
return body;
}
就是你添加了@ControllerAdvice注解的 ResponseBodyAdvice 实现类在这里会被调用
**2、** `body`非空,则进行写入,如果没有`Content-Disposition`请求头,则尝试添加一个,关于文件相关的内容;
**3、** 调用当前HttpMessageConverter实现类的`write(Tt,@NullableTypetype,@NullableMediaTypecontentType,HttpOutputMessageoutputMessage)`方法,将`body`写入响应中;
**4、** `return`返回,结束整个逻辑;
**2、** 到达了此处,说明第`4`步没有找到对应的MediaType对象,或者第`5`步没有一个HttpMessageConverter实现类支持处理该返回结果;
如果 `body` 不为空,也就是说有返回值但是没有处理,则抛出 HttpMediaTypeNotAcceptableException 异常
--------------------
虽然上面的方法很长,但是不难理解,大致逻辑就是找到合适的 HttpMessageConverter 实现类去将返回结果写入到响应体中😈
但是HttpMessageConverter 怎么才合适,怎么写入到响应体中,没有展开讨论,涉及到的内容不少,就在下一篇文档[**《HandlerAdapter 组件(五)之 HttpMessageConverter》**][HandlerAdapter _ HttpMessageConverter]中分析吧
### ViewNameMethodReturnValueHandler ###
`org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler`,实现 HandlerMethodReturnValueHandler 接口,处理返回结果是视图名的 ReturnValueHandler 实现类。
ViewNameMethodReturnValueHandler 适用于前后端未分离,Controller 返回视图名的场景,例如 JSP、Freemarker 等等。
#### 构造方法 ####
```java
public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
/**
* 重定向的表达式的数组
*/
@Nullable
private String[] redirectPatterns;
protected boolean isRedirectViewName(String viewName) {
// 符合 redirectPatterns 表达式,或者以 redirect: 开头
return (PatternMatchUtils.simpleMatch(this.redirectPatterns, viewName) || viewName.startsWith("redirect:"));
}
}
redirectPatterns
:重定向的表达式的数组,用于判断某个视图是否为重定向的视图,一般情况下,不进行设置。
可以看到isRedirectViewName(String viewName)
方法,判断某个视图是否为重定向的视图,如果视图名以 redirect:
开头,也是重定向的视图
supportsReturnType
实现supportsReturnType(MethodParameter returnType)
方法,判断是否支持处理该返回类型,方法如下:
@Override
public boolean supportsReturnType(MethodParameter returnType) {
Class<?> paramType = returnType.getParameterType();
return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
}
该方法的返回类型是否为void
或者字符串
你是否会疑惑?如果我返回的是字符串,想要使用 RequestResponseBodyMethodProcessor 怎么办,不会有问题吗?
回到《HandlerAdapter 组件(一)之 HandlerAdapter》的RequestMappingHandlerAdapter的1.4 getDefaultReturnValueHandlers方法中,如下:
```java private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() { List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(); // ... 省略其他 HandlerMethodReturnValueHandler 实现类的添加 handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.contentNegotiationManager, this.requestResponseBodyAdvice)); // Multi-purpose return value types handlers.add(new ViewNameMethodReturnValueHandler()); // ... 省略其他 HandlerMethodReturnValueHandler 实现类的添加 return handlers; }
>
>RequestResponseBodyMethodProcessor 是在 ViewNameMethodReturnValueHandler 之前添加的,所以不会出现上述问题
#### handleReturnValue ####
实现`handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest)` 方法,代码如下:
```java
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 如果是 String 类型
if (returnValue instanceof CharSequence) {
// 设置视图名到 mavContainer 中
String viewName = returnValue.toString();
mavContainer.setViewName(viewName);
// 如果是重定向,则标记到 mavContainer 中
if (isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}
// 如果是非 String 类型,而且非 void ,则抛出 UnsupportedOperationException 异常
else if (returnValue != null) {
// should not happen
throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
}
}
- 如果返回结果是String类型,则作为视图名设置到 mavContainer 中
- 如果是重定向,则标记到 mavContainer 中的 redirectModelScenario 属性中为 true
注意,此时 mavContainer
的 requestHandled
属性,并未并未像 RequestResponseBodyMethodProcessor 一样,设置为 true
表示请求已处理
这是为什么呢?因为返回结果是视图名的场景下,需要使用 ViewResolver 从 ModelAndView 对象中解析出其对应的视图 View 对象,然后执行 View#render(Map<String, ?>
model, HttpServletRequest request, HttpServletResponse response) 方法,进行渲染。如果你设置为 true,在后续获取到的 ModelAndView 对象就为null了,无法渲染视图
总结
在HandlerAdapter
执行 HandlerMethod
处理器的过程中,会将该处理器封装成 ServletInvocableHandlerMethod
对象,通过该对象来执行处理器。该对象通过反射机制调用对应的方法,在调用方法之前,借助 HandlerMethodArgumentResolver
参数解析器从请求中获取到对应的方法参数值,在调用方法之后,需要借助于HandlerMethodReturnValueHandler 返回值处理器将返回结果设置到响应中,或者设置相应的 Model 和 View 用于后续的视图渲染。
HandlerMethodReturnValueHandler 返回值处理器的实现类非常多,采用了组合模式来进行处理,如果有某一个返回值处理器支持处理该返回值类型,则使用它对返回结果进行处理,例如将返回结果写入响应体中。注意,这里有一定的先后顺序,因为是通过 ArrayList 保存所有的实现类,排在前面的实现类则优先处理。
本文分析了我们常用的 @ResponseBody
注解和前后端未分离时返回视图名两种处理方式,对应的 HandlerMethodReturnValueHandler 实现类,如下:
- RequestResponseBodyMethodProcessor:处理方法参数添加了 @RequestBody 注解方法入参,或者处理方法添加了 @ResponseBody 注解的返回值。在前后端分离之后,后端基本是提供 Restful API ,所以这种方式成为了目前最常用的 HandlerMethodReturnValueHandler 实现类
1、 核心逻辑不复杂,主要是通过HttpMessageConverter
实现类从请求体中获取方法入参或者将返回结果设置到响应体中,关于HttpMessageConverter
相关内容在下一篇文档[《HandlerAdapter组件(五)之HttpMessageConverter》][HandlerAdapter_HttpMessageConverter]中分析;
2、 在处理返回结果时,会将ModelAndViewContainer
的requestHandled
属性设置为true
,表示请求已经处理完成了,后续获取ModelAndView
对象时直接返回null
,不会进行视图渲染,也就和前端分离了~;
- ViewNameMethodReturnValueHandler:处理返回结果是视图名的 HandlerMethodReturnValueHandler实现类
1、 如果你的方法返回值时void
或者字符串
,该类都可以处理,将你的返回结果直接设置为视图名;
2、 这里不会将ModelAndViewContainer
的requestHandled
属性设置为true
,因为后续需要获取ModelAndView
对象进行视图渲染;
你是否会疑惑?如果我返回的是字符串不是视图名,被
ViewNameMethodReturnValueHandler
处理了怎么办?放心,在
HandlerMethodReturnValueHandlerComposite
中判断是否支持处理该返回结果中,会遍历所有的 HandlerMethodReturnValueHandler 实现类,而RequestResponseBodyMethodProcessor
排在ViewNameMethodReturnValueHandler
前面,所以优先交给前者处理。至于为什么
RequestResponseBodyMethodProcessor
排在前面在本文中已经讲过了,因为所有的 HandlerMethodReturnValueHandler 实现类用 ArrayList 集合保存,RequestResponseBodyMethodProcessor
默认先添加进去😈
参考文章:芋道源码《死磕 Spring MVC 源码分析》
版权声明:本文不是「本站」原创文章,版权归原作者所有 | 原文地址: