Spring源码分析-06-SpringMVC- 处理request请求

image-20210428173927324

文章简介:

本文通过一条request请求(/login) 详细描述了 SpringMVC 处理request,相应response的流程.

本文通过图片,文字描述,SpringMVC源码的方式来描述该过程,希望读者能够有所收获.

**文章可能存在语言描述问题,如出现此类错误,还希望读者可以指出错误,以帮助作者完善文章. **

  • PS: 若文章字体偏大或者偏小,建议通过 ctrl键+鼠标滑轮 进行修改,以提升阅读效果.(带来不便,请谅解!)

Version:

  • jdk 1.8
  • Spring Boot: 2.3.4
  • 参考代码 https://gitee.com/leifengyang/springboot2.git

测试案例:

案例: 用户登录

@Controller
public class UserController{
@PostMapping("/login")
    public String main(User user, HttpSession session, Model model){ //RedirectAttributes

        if(StringUtils.hasLength(user.getUserName()) && "123456".equals(user.getPassword())){
            //把登陆成功的用户保存起来
            session.setAttribute("loginUser",user);
            //登录成功重定向到main.html;  重定向防止表单重复提交
            return "redirect:/main.html";
        }else {
            model.addAttribute("msg","账号密码错误");
            //回到登录页面
            return "login";
        }

    }    
}

案例结果:

页面重定向到 localhost:8080/main.html

注意: 源码分析流程 以此为主.


人话描述流程:

客户端发送请求到SpringMVC相应请求时序图:

spring mvc

本图较为仔细的给出了 request 的跳转流程. 仅做认知即可,不需要特别关注该过程.

堆栈信息图:

image-20210428173950470

总体流程:


即DispatcherServlet 调用doDispatch() 的过程.

  1. 获取 webAsyncManager ,处理异步请求 的一个东西.(本文章内不考虑此过程)

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

  2. 处理 request 请求

    1. 处理文件上传请求 即 processedRequest = checkMultipart(request);

    2. 获取 HandlerExecutionChain处理器执行链 即 mappedhandler(包含拦截器链Interceptors)

      • mappedHandler = getHandler(processedRequest);//mappedHandler 与XXXController 相关联
    3. 为当前 request 请求 寻找Adapter (对于整体流程而言不重要)

    4. 处理 get请求的header 方法(跳过,不重要)

    5. 正序 处理mappedHandler(HandlerExecutionchain) 中拦截器链的preHandle方法.

  • mappedHandler.applyPreHandle(processedRequest, response)
  1. 真正处理request请求方法(重要!!!)

    • mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    1. 倒序 处理 拦截器链的postHandle() 方法

      • mappedHandler.applyPostHandle(processedRequest, response, mv);
  2. 处理返回结果 modelAndView 即mv , 并渲染页面(重要)

    processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

补充: 步骤2 和步骤3 通过try catch 方式运行, catch 异常后, 倒序遍历 拦截器链Interceptors , 执行aftercompletion()方法


主要流程:

拦截器链的调用流程:

image-20210428145007988

图片来自 网络 地址:here

try{
    //1. 正序 执行preHandle
    mappedHandler.applyPreHandle(processedRequest, response)
    //2. 真正处理请求
        mv  = ha.handle(processedRequest, response, mappedHandler.getHandler());
    // 3, 倒序 执行 postHandle
    mappedHandler.applyPostHandle(processedRequest, response, mv);
}catch (Exception e){// 倒序执行 afterCompletion() 方法
    triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}

由于本部分内容源码过于简单, 不再赘述.


真正处理request请求的流程:

invokeHandlerMethod() 方法流程.

PS 省略掉部分内容(包含HTTPSession判断, 本文只关心主线)

  1. 设置属性 (不重要)

    1. 设置 请求参数处理器 即 argumentResolvers
    2. 设置 结果返回值处理器, 即returnValueHandlers
    3. 设置其他属性值…..
  2. 执行目标方法invocableMethod.invokeAndHandle(webRequest, mavContainer);

    1. 执行 request 请求, 即执行 XXXController 对应方法(不过需要先处理方法参数) 即 invokeForRequest() 重要!!!
      • 处理方法参数getMethodArgumentValues(); //重要
      • 执行目标方法 doInvoke() . 即通过反射调用XXXController 与之对应的方法.
    2. 处理返回值 放入mavContainer中. 即this.returnValueHandlers.handleReturnValue()
  3. 根据mavContainerrequest生成modelAndView

    getModelAndView(mavContainer, modelFactory, webRequest);

补充说明: args 参数处理流程 和 返回值处理流程大致相似, 下面以参数处理流程进行描述.

args方法参数处理流程:
  1. 获取方法参数

    ​ MethodParameter[] parameters = getMethodParameters();

  2. 依次遍历paramters, 对每个方法参数parameter 调用与其适配的argResolver进行参数处理.

    args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);

    • 遍历 this.argumentResolvers ,寻找 可以处理方法参数 parameter的HandlerMethodArgumentResolver

    • 调用 handlerMethodArgumentResolver.resolveArgument() 方法 处理当前参数

PS: 源码分析过程中给出 结果返回值处理过程(找resolver, 然后执行handleReturnValue() 方法


处理放回结果mv ,渲染页面流程:

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

  1. 判断是否有exception ,有的话生成errorView ,并返回

  2. render(mv,request,response) 渲染页面

    1. 设置response.locale

    2. 根据viewName 生成view 对象(页面)

    3. 渲染生成的view页面(这里以RedirectView页面为例)

      • 生成model 即 Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);

      • 将model与view绑定RedirectView为例

        renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);

        1. 生成 url
        2. 调用response.sendRedirect(url);
  3. 倒序执行拦截器链 **afterCompletion()**方法


补充说明:

参数处理器类图

image-20210428162002309

返回值处理器类图

image-20210428161852979

源码分析:

总流程:

  • DispatcherServlet.class
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;
		//1.  获取 异步请求 管理器  webAsyncManager
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;
		// 2. 处理请求
			try {// 2.1 检查是否是文件上传,如果文件需要上传,返回 可以处理文件传输的request MultipartHttpServletRequest
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);
				//2.2 获取  HandlerExecutionChain 处理器执行链( 包含拦截器列表)  (mappedHandler  与 XXXController.class相关联)
				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}
				// 2.3 为当前 request 请求 寻找Adapter
				// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {// 处理 request head 请求
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}
				//2.4 正序 处理拦截器链中每个 Interceptor 的 前置方法 preHandle(), 若返回为false ,倒序执行
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return; // 如果被拦截, 直接返回, 不对请求进行处理
				}
				// 2.5 真正处理请求 的方法 . 生成 modelAndView
				// Actually invoke the handler.
				mv  = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
				//2.6  倒序 处理拦截器链中每个Interceptor 的 后置方法, postHandle()
				applyDefaultViewName(processedRequest, mv); //设置viewName
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				// As of 4.3, we're processing Errors thrown from handler methods as well,
				// making them available for @ExceptionHandler methods and other scenarios.
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}// 2.7 处理 返回结果 并 渲染页面. 从定向,response
 			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {//  捕获异常,并 倒序遍历Interceptor list 执行 aftercompletion() method
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

拦截器链调用:

由于本部分内容源码过于简单, 不再赘述.

真正处理request请求:

  • RequestMappingHandlerAdapter.invokHandlerMethod()

  • RequestMappingHandlerAdapter.class

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
		//  生成 servletwebRequest
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try { //1 .  参数设置
			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
			ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

			ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
			if (this.argumentResolvers != null) {//1.1设置请求参数处理器
				invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
			}
			if (this.returnValueHandlers != null) {//1.2设置结果返回值处理器
				invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
			}
            // 省略掉部分代码. 设置webAsyncManager 
			// 2. 方法执行
			invocableMethod.invokeAndHandle(webRequest, mavContainer);
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}
			// 3.根据 mavContainer ,webRequest 生成 modelAndView
			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			webRequest.requestCompleted();
		}
	}

参数设置:

1. 设置参数处理器
2. 设置返回值处理器
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//.... 		
		if (this.argumentResolvers != null) {//1.1设置请求参数处理器
				invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
			}
			if (this.returnValueHandlers != null) {//1.2设置结果返回值处理器
				invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
			}
/// .....
}

执行目标方法

invocableMethod.invokeAndHandle(webRequest, mavContainer);

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {
   //1, 执行 request 请求, 即执行 XXXController 对应方法(不过需要先处理方法参数)
   Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
   setResponseStatus(webRequest);// 设置response status
   // 处理返回值为空的情况
   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;
   }
   //2 . 将返回值 与mav 绑定
   mavContainer.setRequestHandled(false);
   Assert.state(this.returnValueHandlers != null, "No return value handlers");
   try {// 就是这个方法
      this.returnValueHandlers.handleReturnValue(
            returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
   }
   catch (Exception ex) {
      if (logger.isTraceEnabled()) {
         logger.trace(formatErrorForReturnValue(returnValue), ex);
      }
      throw ex;
   }
}
执行request请求:
  • InvocableHandlerMethod.class
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {
//1 . 处理方法请求参数 , argmentResolver
   Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
   if (logger.isTraceEnabled()) {
      logger.trace("Arguments: " + Arrays.toString(args));
   }//2 .执行 方法 XXXController 与之对应的方法
   return doInvoke(args);
}
返回值放入mvContainer中
  • HandlerMethodReturnValueHandlerComposite.class
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
	// 遍历this.returnValueHandlers 寻找合适的 returnvalueHandler
	HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
	if (handler == null) {
		throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
	}// 处理 returnvalue (原理, 单一接口继承原则, 不同借口,不同方法)
	handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

ViewNameMethodReturnValueHandler.class为例

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
	// 判断returnValue 是否是字符串
	if (returnValue instanceof CharSequence) {
		String viewName = returnValue.toString();
		mavContainer.setViewName(viewName);
		if (isRedirectViewName(viewName)) {
			mavContainer.setRedirectModelScenario(true);
		}
	}
	else if (returnValue != null) {
		// should not happen
		throw new UnsupportedOperationException("Unexpected return type: " +
				returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
	}
}

生成ModelAndView

  • RequestMappingHandlerAdapter.class
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
			ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

		modelFactory.updateModel(webRequest, mavContainer);
		if (mavContainer.isRequestHandled()) {
			return null;
		}
		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;
	}

处理放回结果mv ,渲染页面:

  • DispatcherServlet.class
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
      @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
      @Nullable Exception exception) throws Exception {

   boolean errorView = false;
   // 1. 判断是否出现Exception 生成errorView
   if (exception != null) {
      if (exception instanceof ModelAndViewDefiningException) {
         logger.debug("ModelAndViewDefiningException encountered", exception);
         mv = ((ModelAndViewDefiningException) exception).getModelAndView();
      }
      else {
         Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
         mv = processHandlerException(request, response, handler, exception);
         errorView = (mv != null);
      }
   }
   // 2.  根据modelAndView 渲染页面
   // Did the handler return a view to render?
   if (mv != null && !mv.wasCleared()) {
      render(mv, request, response);
      if (errorView) {
         WebUtils.clearErrorRequestAttributes(request);
      }
   }
   else {
      if (logger.isTraceEnabled()) {
         logger.trace("No view rendering, null ModelAndView returned.");
      }
   }

   if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
      // Concurrent handling started during a forward
      return;
   }
   //3. 倒序执行 拦截器链的 afterCompletion() 方法
   if (mappedHandler != null) {
      // E xception (if any) is already handled..
      mappedHandler.triggerAfterCompletion(request, response, null);
   }
}

生成view

  • DispatcherServlet.class
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
	// Determine locale for request and apply it to the response.//1.设置response.locale
	Locale locale =
			(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
	response.setLocale(locale);
	//2. 根据viewName 生成 对应的view 对象
	View view;
	String viewName = mv.getViewName();
	if (viewName != null) {
		// We need to resolve the view name.
		view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
		if (view == null) {
			throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
					"' in servlet with name '" + getServletName() + "'");
		}
	}
	else {
		// No need to lookup: the ModelAndView object contains the actual View object.
		view = mv.getView();
		if (view == null) {
			throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
					"View object in servlet with name '" + getServletName() + "'");
		}
	}
	try {
		if (mv.getStatus() != null) {
			response.setStatus(mv.getStatus().value());
		}//3. 渲染view
		view.render(mv.getModelInternal(), request, response);
	}
	catch (Exception ex) {
		if (logger.isDebugEnabled()) {
			logger.debug("Error rendering view [" + view + "]", ex);
		}
		throw ex;
	}
}

view.render()

  • AbstractView.class ( 模板方法 , 最后合并model 和view 是到子类去做的)
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
		HttpServletResponse response) throws Exception {

	if (logger.isDebugEnabled()) {
		logger.debug("View " + formatViewName() +
				", model " + (model != null ? model : Collections.emptyMap()) +
				(this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));
	}
	// 1. 生成model
	Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
	prepareResponse(request, response);
	renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}

RedirectView 的具体做法

@Override
	protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
			HttpServletResponse response) throws IOException {

		String targetUrl = createTargetUrl(model, request);
		targetUrl = updateTargetUrl(targetUrl, model, request, response);

		// Save flash attributes
		RequestContextUtils.saveOutputFlashMap(targetUrl, request, response);

		// Redirect// 调用这个东西response.sendRedirect(encodedURL);
		sendRedirect(request, response, targetUrl, this.http10Compatible);
        
	}

总结:

  • 总流程:
    1. request 初始化 ( 判断 文件上传请求, 异步请求,request 与controller 绑定)
    2. request 执行流程(拦截器链HandlerExecutionChain, 方法参数处理,方法返回值处理. 生成modelandview)
    3. modelAndView 生成渲染页面 ( 重定向: response.sendRedirect() , 转发:request.getRequestDispatcher(“/“).forward(request,response);)
  • 省略内容:
    • 由于本次源码分析是结果是基于重定向的情况,所以少了**内容协商(比如返回值是json 字符串,需要进行类型转换) **, 有需要建议去参考链接查看. 我给出的源码解析较为简单,请见谅!

参考:

  • https://www.yuque.com/atguigu/springboot/vgzmgh