一文让你彻底搞懂 Spring MVC 的执行流程

 2022-07-25

来自官网:

202207252336486681.png

在网上找到的更加详细的图:

202207252336502392.png

我们搜索下DispatchServlet类看看有没有,果然有一个

202207252336511263.png

看下DispatchServlet的类关系图

202207252336518084.png

1 研究DispatchServlet

看下这个类的全部方法,不用想一定会有一个类似于Servlet中的service方法,果然如此

202207252336524855.png

1.1 DispatchServlet的doService方法

在doService方法中一定是主要处理业务请求的

    @Override
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
      // 记录日志
      logRequest(request);
      // 保留请求属性的快照,以便在包含之后能够恢复原始属性 
      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));
          }
        }
      }
      // 设置一些request的值,使框架对象对处理程序和视图对象可用
      request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); 
      ......
      try {
        //最主要的doDispatch方法
        doDispatch(request, response);
      }
      ......
    }

1.2 DispatchServlet的doDispatch方法

doDispatch是被doService方法调用,是处理请求流程的一个主要方法,主要分为一下几个流程

202207252336533706.png

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
      ......
      try {
        ModelAndView mv = null;
        Exception dispatchException = null;
        try {
          //检查处理Multipart请求
          processedRequest = checkMultipart(request);
          multipartRequestParsed = (processedRequest != request);
          // 确定当前请求的处理,请求Handler
          mappedHandler = getHandler(processedRequest);
          if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
          }
          // 获取适配器
          HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
          // 判断请求方法
          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;
            }
          }
          if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
          }
          // 实际的Handler调用
          mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
          if (asyncManager.isConcurrentHandlingStarted()) {
            return;
          }
          // 解析ModelAndView
          applyDefaultViewName(processedRequest, mv);
          mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        ......
        // 渲染视图
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
      }
      ......
      finally {
        ......
      }
      }

2 深入源码

2.1 怎样找到Handler的?

先写一个RequestMapping

    @ResponseBody
    @RequestMapping("/helloMvc")
    public String helloMvc() {
      return "Hello I am Spring MVC";
    }

打断点,debug

202207252336542337.png

请求

202207252336548678.png

开始debug

202207252336552919.png

点进方法内部再看看

2022072523365610910.png

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
      if (this.handlerMappings != null) {
        // 遍历所有的request,直到最后都没有的话返回null
        for (HandlerMapping mapping : this.handlerMappings) {
          HandlerExecutionChain handler = mapping.getHandler(request);
          if (handler != null) {
            return handler;
          }
        }
      }
      return null;
    }

在这之后,请求就会找到对应的Handler进行处理。

2.2 Spring MVC视图解析机制

首先定义一个ViewResolver类型的List

    /** List of ViewResolvers used by this servlet. */
    @Nullable
    private List<ViewResolver> viewResolvers;
    初始化ViewResolvers
    private void initViewResolvers(ApplicationContext context) {
      this.viewResolvers = null;
      if (this.detectAllViewResolvers) {
        // 在ApplicationContext中找到所有的viewresolver,包括全部上下文
        Map<String, ViewResolver> matchingBeans =
          BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
        if (!matchingBeans.isEmpty()) {
          this.viewResolvers = new ArrayList<>(matchingBeans.values());
          // 保持viewresolver的排序。
          AnnotationAwareOrderComparator.sort(this.viewResolvers);
        }
      }
      ......
    }
      然后进行视图解析
      private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                         @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
                                         @Nullable Exception exception) throws Exception {
        boolean errorView = false;
        // 判断有无异常
        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);
          }
        }
        if (mv != null && !mv.wasCleared()) {
          // 视图渲染
          render(mv, request, response);
          if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
          }
        }
        ......
      }
        视图渲染
        protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
          // 确定请求的区域设置并将其应用于响应
          Locale locale =
            (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
          response.setLocale(locale);
          View view;
          String viewName = mv.getViewName();
          if (viewName != null) {
            // 获取视图名称
            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());
            }
            view.render(mv.getModelInternal(), request, response);
          }
          catch (Exception ex) {
            if (logger.isDebugEnabled()) {
              logger.debug("Error rendering view [" + view + "]", ex);
            }
            throw ex;
          }
        }