本系列文章的上一篇 : Spring MVC : 控制器方法处理请求的过程分析 - 6. 控制器方法返回值处理
在上一篇文章中,我们讲解了ServletInvocableHandlerMethod#invokeAndHandle
接收到控制器方法返回值之后,对返回值进行处理的逻辑。在该逻辑过程中,如果ServletInvocableHandlerMethod#invokeAndHandle
基于上下文和组件HandlerMethodReturnValueHandler
认为请求处理已经完成,则会将mavContainer.requestHandled
属性设置为true
,否则,程序会将控制权交给调用者,继续请求处理过程。这里,调用者就是RequestMappingHandlerAdapter#invokeHandlerMethod
,我们看一下相关代码 :
// RequestMappingHandlerAdapter#invokeHandlerMethod 代码片段
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// 这里执行请求参数解析,目标控制器方法调用,参数验证方法拦截器应用和返回值的处理
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 逻辑能走到这里,说明请求尚未被 invocableMethod 及各种 argumentResolvers,
// returnValueHandlers 处理完成,这里需要继续处理,该处理过程是本文讲述的
// 重点
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
从以上代码可见,如果ServletInvocableHandlerMethod#invokeAndHandle
未能将请求处理完成,接下来,RequestMappingHandlerAdapter#invokeHandlerMethod
会调用getModelAndView
并返回其结果给上一层的调用者。实际上,方法getModelAndVie
·所做的事情,主要是将控制器方法执行过程中保存在mavContainer
中的决定信息包装成一个ModelAndView
对象。其具体逻辑如下 :
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
// 如果 mavContainer.isRequestHandled() 为真,说明上一篇中介绍的逻辑已经将请求处理完了,
// 所以这里返回 null,这里的返回 null 有两点意思需要指明 :
// 1. 当前方法以下构造 ModelAndView 的主逻辑相当于不用执行了;
// 2. 遇到 null ModelAndView,调用者会认为请求已经处理完成,不会再继续处理该请求;
return null;
}
// 逻辑能走到这里的话,说明之前的处理逻辑并未将请求处理完成,所以现在需要根据之前的执行结果
// 构造 ModelAndView 对象,这是留给调用者 DispatcherServlet 用于解析和渲染视图的数据载体。
ModelMap model = mavContainer.getModel();
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;
}
相应地,调用者DispatcherServlet
得到该ModelAndView
之后,就是进行相应的视图解析和渲染了,具体的可以参考 :
Spring MVC : DispatcherServlet请求处理的主逻辑(文字版)
本系列文章链接合集 :