Spring MVC : 控制器方法处理请求的过程分析 - 0. 概述

Spring MVC中,一般来讲,控制器指的是开发人员使用了注解@Controller这样的类,而控制器方法,是控制器类中使用了注解@RequestMapping的那些方法。控制器方法能处理哪些请求,就是通过这些@RequestMapping注解信息来定义的,DispatcherServlet会把这些信息加载保存到一个RequestMappingHandlerMapping对象中,当相应的请求到达时,DispatcherServlet会通过该信息找到相应的控制器方法,然后通过调用RequestMappingHandlerAdapter执行找到的控制器方法,完成对请求的处理。

RequestMappingHandlerAdapter执行控制器方法的过程,听起来很简单,似乎就是一个一般方法调用的问题,其实不然,这里面牵涉到相当多的步骤和逻辑,如果不了解这些步骤和逻辑,遇到问题时我们通常会无从下手。而如果对此过程透彻了解,当问题再出现的时候,我们就能够轻松推断出问题所在从而迅速解决,甚至能提前准备预防问题发生。

这个系列的文章,就打算结合源代码,系统性地分析一下通过RequestMappingHandlerAdapter执行控制器方法处理请求的整个过程。这个过程大致分为如下几个步骤,每个步骤将对应一篇文章 :

  1. 执行信息记录对象ModelAndViewContainer的准备
  2. 请求参数的获取
  3. 控制器方法参数值绑定 HandlerMethodArgumentResolver
  4. 控制器方法参数值的验证 MethodValidationInterceptor
  5. 调用控制器方法本身
  6. 控制器方法返回值处理
  7. 包装返回结果 : 从ModelAndViewContainer对象构造ModelAndView对象

读完该系列文章,相信你对控制器方法处理请求的整个过程,会有足够深入透彻的理解。

注意,HandlerInterceptor的应用不在本系列文章的关注范围之内,如果想了解HandlerInterceptor的应用,可以参考 :DispatcherServlet 请求处理主逻辑 : 3. 通过 HandlerAdapter 执行 Handler

在分篇讲解之前,我们先看一下RequestMappingHandlerAdapter调用控制器方法的入口 :

    // RequestMappingHandlerAdapter 代码片段
    // 返回值是一个 ModelAndView
    // 如果需要调用者后续解析和渲染视图,则该返回值 ModelAndView 不会为 null
    // 如果请求已经被该方法完全处理,不需要调用者后续解析和渲染视图,则返回值 ModelAndView 会是 null
	@Nullable
	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

       // 将  HttpServletRequest/HttpServletResponse 对象包装成一个 ServletWebRequest 对象
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try {
          // 准备工作,设置调用控制器方法所需要的各种数据,或者工具组件
			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
			ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

           // 将目标控制器方法 handlerMethod 包装成一个 ServletInvocableHandlerMethod,
           // 这里主要是针对 Servlet 环境的一些特殊处理和包装,参数传递
			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 是一个调用目标控制器方法过程中,记录参数解析,返回值处理的一些
          // 决策信息的一个容器类型/持有器类型,这里准备出这样一个对象,供控制器方法调用过程中使用
			ModelAndViewContainer mavContainer = new ModelAndViewContainer();
          // 将请求中可能存在的 FlashMap 属性添加到 mavContainer
			mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
          // 1. 获取所有 @SessionAttributes 属性到   mavContainer
          // 2. 执行所有 @ModelAttribute 方法
          // 3. 找到所有使用了注解 @ModelAttribute 并且也属于 @SessionAttributes 列表的
          //    参数将其添加到 mavContainer
			modelFactory.initModel(webRequest, mavContainer, invocableMethod);
          // 设置重定向时如果 RedirectAttributes 属性未设置,是否要使用缺省 Model,
          // 缺省值为 false, 表示即使重定向时 RedirectAttributes 属性未设置,也不要使用缺省 Model
			mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

          // 对异步Web请求的处理的准备工作,本系列文章中只介绍同步请求处理,所以这里忽略对这一部分的介绍  
			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;
			}

          // 目标控制器方法执行完结,请求处理已经完成,执行过程中的各种决定信息都记录在了 
          // mavContainer 中,现在结合相关组件构造一个 ModelAndView 对象,后续调用者
          // 有可能还有基于这些信息解析和渲染视图
          // 如果控制器方法已经完全处理了该请求,无需后续处理,则 mavContainer 会包含
          // 这样的信息 : #isRequestHandled == true, 从而这里的 ModelAndView 返回值
          // 会是 null
			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			webRequest.requestCompleted();
		}
	}

本系列文章链接合集 :

  1. 概述
  2. 执行信息记录对象ModelAndViewContainer的准备
  3. 请求参数的获取
  4. 控制器方法参数值绑定 HandlerMethodArgumentResolver
  5. 控制器方法参数值的验证 MethodValidationInterceptor
  6. 调用控制器方法本身
  7. 控制器方法返回值处理
  8. 包装返回结果 : 从ModelAndViewContainer对象构造ModelAndView对象

参考文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值