handler method 的返回值处理包含两个方面:
一是:返回值对象的处理;
二是:视图的渲染。
返回值对象的处理
通过前面的分析,我们知道, 被 @RequestMapping 标记 handler method 的执行是通过调用 RequestMappingHandlerAdapter#handle()
。
其中,参数解析、handler method 的执行 和 对返回值的处理,最终是通过 ServletInvocableHandlerMethod#invokeAndHandle()
来处理的。
具体的调用过程如下:
ServletInvocableHandlerMethod#invokeAndHandle()
的处理过程如下:
所以,handler method 返回的值对象会被 HandlerMethodReturnValueHandler
来处理。
HandlerMethodReturnValueHandler
HandlerMethodReturnValueHandler 是处理 handler method 返回值的策略接口。
其中,RequestResponseBodyMethodProcessor
是用来处理 @ResponseBody
类型的返回值的;
ModelAndViewResolverMethodReturnValueHandler
是一个最终兜底的返回值处理类,它是在所有其他的处理器之后执行的。
此处略去了跟异步请求相关的 ReturnValueHandler :
StreamingResponseBodyReturnValueHandler
CallableMethodReturnValueHandler
AsyncTaskMethodReturnValueHandler
视图的渲染
视图的渲染过程是在 DispatcherServlet
中进行处理的,具体的处理代码如下:
DispatcherServlet#render()
会将 ModelAndView 进行渲染。 这是处理请求的最后一个阶段。
可以看到,视图渲染的过程如下:
1、通过 ViewResolver 解析 viewName 对应的 View 对象
2、调用 View#render() 渲染视图,呈现给用户
@ResponseBody 标记的方法会经过视图解析吗?
先说结论:@ResponseBody 标记的方法不会经过视图解析。
原因分析:
@ResponseBody
类型的返回值是通过 RequestResponseBodyMethodProcessor
来处理的。
RequestResponseBodyMethodProcessor#handleReturnValue
会设置标志位 requestHandled=true
,标记请求已被直接处理,不需要视图解析。
这样的话,RequestMappingHandlerAdapter#invokeHandlerMethod()
返回的 ModelAndView 就是 null,DispatcherServlet 就不会进行视图解析。
RequestResponseBodyMethodProcessor
的处理过程如下:
// RequestResponseBodyMethodProcessor#handleReturnValue
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
// 标记标记请求已被直接处理,不需要视图解析
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
小结
返回值的处理包括返回值对象的处理和视图的渲染。
其中,handler method 的返回值对象会通过 HandlerMethodReturnValueHandler
来处理。
如果需要视图渲染的话,就会在 DispatcherServlet#processDispatchResult()
中进行统一处理,通过 ViewResolver
解析出相应的 View 视图,最终调用 View#render()
渲染视图,呈现给用户。