ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

面试官:小伙子,你画的SpringMVC请求处理过程是从网上抄的吧?

2021-12-14 13:00:29  阅读:190  来源: 互联网

标签:面试官 处理过程 SpringMVC mappedHandler request 视图 Handler response processedRequest


  推荐学习肝了十天半月,献上纯手绘“Spring/Cloud/Boot/MVC”全家桶脑图 消息中间件合集:MQ(ActiveMQ/RabbitMQ/RocketMQ)+Kafka+笔记 前言

  SpringMVC请求处理相信大家都很熟悉了,本篇主要是基于SpringMVC处理请求的流程来阅读并调试源码,以及解决几个仅靠流程图无法解释的问题。

  本篇使用的Spring版本为5.2.2.RELEASE

  九大组件

  SpringMVC几乎所有的功能都由九大组件来完成,所以明白九大组件的作用,对于学习SpringMVC来说非常重要。

  /** 文件上传解析器 */

  private MultipartResolver multipartResolver;

  /** 区域解析器,用于国际化 */

  private LocaleResolver localeResolver;

  /** 主题解析器 */

  private ThemeResolver themeResolver;

  /** Handler映射信息 */

  private List handlerMappings;

  /** Handler适配器*/

  private List handlerAdapters;

  /** Handler执行异常解析器 */

  private List handlerExceptionResolvers;

  /** 请求到视图的转换器 */

  private RequestToViewNameTranslator viewNameTranslator;

  /** SpringMVC允许重定向时携带参数,存在session中,用完就销毁,所以叫FlashMap */

  private FlashMapManager flashMapManager;

  /** 视图解析器 */

  private List viewResolvers;

  1234567891011121314151617181920212223242526HandlerMapping:Handler映射信息,根据请求携带的url信息查找处理器(Handler)。每个请求都需要对应的Handler处理。HandlerAdapter:Handler适配器,SpringMVC没有直接调用处理器(Handler),而是通过HandlerAdapter来调用,主要是为了统一Handler的调用方式ViewResolver:视图解析器,用来将字符串类型的视图名称解析为View类型的视图。ViewResolver需要找到渲染所用的模板和所用的技术(也就是视图的类型)进行渲染,具体的渲染过程则交由不同的视图自己完成。MultipartResolver:文件上传解析器,主要用来处理文件上传请求HandlerExceptionResolver:Handler执行异常解析器,用来对异常进行统一处理RequestToViewNameTranslator:请求到视图的转换器LocaleResolver:区域解析器,用于支持国际化FlashMapManager:SpringMVC允许重定向时携带参数,存在session中,用完就销毁,所以叫FlashMapThemeResolver:主题解析器,用于支持不同的主题

  九大组件中最重的的前三个,HandlerMapping、HandlerAdapter和ViewResolver,因为这是阅读源码时,避不开的三个组件。

  调试准备

  搭建一个基本的Spring web项目即可

  Controller部分

  @Controller

  public class IndexController {

  @RequestMapping("/index/home")

  public String home(String id, Student student, @RequestParam("code") String code) {

  System.out.println(student.getName());

  return "index";

  }

  @ResponseBody

  @RequestMapping("/index/list")

  public String list() {

  return "success";

  }

  }

  Entity部分

  public class Student {

  private String name;

  private Integer gender;

  // getter、setter

  }

  还是那句话,Spring源码非常庞大,不能只见树木不见森林,需要有针对性的阅读,所以本篇只需要关注主体流程即可。

  核心方法

  我们都知道,SpringMVC有一个用来分发请求的前端控制器DispatcherServlet,其中用来处理请求的方法就是doService,该方法定义如下

  doService

  /**

  * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}

  * for the actual dispatching.

  */

  @Override

  protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {

  logRequest(request);

  // Keep a snapshot of the request attributes in case of an include,

  // to be able to restore the original attributes after the include.

  Map<String, Object> attributesSnapshot=null;

  if (WebUtils.isIncludeRequest(request)) {

  attributesSnapshot=new HashMap<>();

  Enumeration<?> attrNames=request.getAttributeNames();

  while (attrNames.hasMoreElements()) {

  String attrName=(String) attrNames.nextElement();

  if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {

  attributesSnapshot.put(attrName, request.getAttribute(attrName));

  }

  }

  }

  // Make framework objects available to handlers and view objects.

  request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());

  request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);

  request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);

  request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

  if (this.flashMapManager !=null) {

  FlashMap inputFlashMap=this.flashMapManager.retrieveAndUpdate(request, response);

  if (inputFlashMap !=null) {

  request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));

  }

  request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());

  request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

  }

  try {

  // 真正执行的方法

  doDispatch(request, response);

  }

  finally {

  if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {

  // Restore the original attribute snapshot, in case of an include.

  if (attributesSnapshot !=null) {

  restoreAttributesAfterInclude(request, attributesSnapshot);

  }

  }

  }

  }doDispatch

  doDispatch是doService中真正用来处理请求的方法

  /**

  * 实际处理请求的方法

  */

  protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {

  HttpServletRequest processedRequest=request;

  HandlerExecutionChain mappedHandler=null;

  boolean multipartRequestParsed=false;

  WebAsyncManager asyncManager=WebAsyncUtils.getAsyncManager(request);

  try {

  ModelAndView mv=null;

  Exception dispatchException=null;

  try {

  // 校验是否是文件上传请求

  processedRequest=checkMultipart(request);

  multipartRequestParsed=(processedRequest !=request);

  // Determine handler for the current request.

  // 为当前请求找到一个合适的处理器(Handler)

  // 返回值是一个HandlerExecutionChain,也就是处理器执行链

  mappedHandler=getHandler(processedRequest);

  if (mappedHandler==null) {

  noHandlerFound(processedRequest, response);

  return;

  }

  // Determine handler adapter for the current request.

  // 根据HandlerExecutionChain携带的Handler找到合适的HandlerAdapter

  HandlerAdapter ha=getHandlerAdapter(mappedHandler.getHandler());

  // Process last-modified header, if supported by the handler.

  // 处理GET请求的缓存

  String method=request.getMethod();

  boolean isGet="GET".equals(method);

  if (isGet || "HEAD".equals(method)) {

  long lastModified=ha.getLastModified(request, mappedHandler.getHandler());

  if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {

  return;

  }

  }

  // 执行拦截器的preHandle方法

  if (!mappedHandler.applyPreHandle(processedRequest, response)) {

  return;

  }

  // Actually invoke the handler.

  // 利用HandlerAdapter来执行Handler里对应的处理方法

  mv=ha.handle(processedRequest, response, mappedHandler.getHandler());

  if (asyncManager.isConcurrentHandlingStarted()) {

  return;

  }

  // 如果没有设置视图,则应用默认的视图名

  applyDefaultViewName(processedRequest, mv);

  // 执行拦截器的postHandle方法

  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);

  }

  // 根据ModelAndView对象解析视图

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

  }

  catch (Exception ex) {

  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(proc

标签:面试官,处理过程,SpringMVC,mappedHandler,request,视图,Handler,response,processedRequest
来源: https://www.cnblogs.com/linjingyg/p/15687374.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有