精尽Spring MVC源码分析 - HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读
Spring 版本:5.1.14.RELEASE
HandlerAdapter 组件
HandlerAdapter 组件,处理器的适配器。因为处理器 handler 的类型是 Object 类型,需要有一个调用者来实现 handler 是怎么被执行。Spring 中的处理器的实现多变,比如用户的处理器可以实现 Controller 接口或者 HttpRequestHandler 接口,也可以用 @RequestMapping 注解将方法作为一个处理器等,这就导致 Spring MVC 无法直接执行这个处理器。所以这里需要一个处理器适配器,由它去执行处理器
由于 HandlerMapping 组件涉及到的内容较多,考虑到内容的排版,所以将这部分内容拆分成了五个模块,依次进行分析:
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参数
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)这个核心方法,大致逻辑如下:
获取使用的 MediaType 对象
contentType从请求头中获取
Content-Type请求头中没有则设置为默认的类型
application/octet-stream获取方法参数的
containing class和targetClass 目标类型,用于 HttpMessageConverter 解析获取 HTTP 方法
开始从请求中解析方法入参
Object body如果请求体不为空,则通过该 HttpMessageConverter 从请求体中解析出方法入参对象
如果请求体为空,则无需解析请求体
将请求消息对象封装成 EmptyBodyCheckingHttpInputMessage,用于校验是否有请求体,没有的话设置为
null遍历所有的 HttpMessageConverter 实现类,调用其
canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType)方法,判断当前 HttpMessageConverter 实现类是否支持解析该方法入参,如果返回true,则使用该 HttpMessageConverter 实现类进行解析注意:上面不管请求体是否为空,都会调用
RequestResponseBodyAdvice的afterBodyRead方法,存在 RequestBodyAdvice 则对方法入参进行修改校验解析出来的方法入参对象是否为空,抛出异常或者返回
null返回方法入参对象
body
方法虽然很长,但是不难理解,大致逻辑就是找到合适的 HttpMessageConverter 实现类从请求体中获取到方法入参对象
逻辑和下面的 writeWithMessageConverters 差不多,我们重点来看到下面这个方法
