Spring MVC : 控制器方法处理请求的过程分析 - 7. 包装返回结果 : ModelAndViewContainer => ModelAndView

本系列文章的上一篇 : 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请求处理的主逻辑(文字版)

本系列文章链接合集 :

  1. 概述
  2. 执行信息记录对象ModelAndViewContainer的准备
  3. 请求参数的获取
  4. 控制器方法参数值绑定 HandlerMethodArgumentResolver
  5. 控制器方法参数值的验证 MethodValidationInterceptor
  6. 调用控制器方法本身
  7. 控制器方法返回值处理
  8. 包装返回结果 : 从ModelAndViewContainer对象构造ModelAndView对象
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值