springMVC execution process and working principle

SpringMVC execution process:

1. The user sends a request to the front-end controller DispatcherServlet
2. DispatcherServlet receives the request and calls the processor mapper HandlerMapping.
3. The processor mapper finds the specific processor according to the request URL, generates the processor execution chain HandlerExecutionChain (including the processor object and processor interceptor) and returns it to the DispatcherServlet.
4. DispatcherServlet obtains the processor adapter HandlerAdapter based on the processor Handler and executes the HandlerAdapter to process a series of operations, such as parameter encapsulation, data format conversion, data verification and other operations.
5. Execution processor Handler (Controller, also called page controller).
6.Handler returns to ModelAndView after execution is completed
7.HandlerAdapter returns the Handler execution result ModelAndView to DispatcherServlet
8.DispatcherServlet passes ModelAndView to ViewReslover view resolver
9.ViewReslover returns the specific View after parsing
10.DispatcherServlet renders the View (that is, fills the model data model into the view).
11.DispatcherServlet responds to the user.

Component description:

1.DispatcherServlet: front-end controller. When the user request reaches the front-end controller, it is equivalent to c in the MVC mode. The dispatcherServlet is the center of the entire process control. It calls other components to handle the user’s request. The existence of the dispatcherServlet reduces the coupling between components and system scalability. improve. Implemented by the framework
2.HandlerMapping: Processor mapper. HandlerMapping is responsible for finding the Handler or processor according to the URL requested by the user. Springmvc provides different mappers to implement different mapping methods, which can be searched according to certain rules, such as xml configuration method, interface implementation method, annotation method, etc. Implemented by the framework
3.Handler: Processor. Handler is the back-end controller following the DispatcherServlet front-end controller. Under the control of DispatcherServlet, the Handler processes specific user requests. Since Handler involves specific user business requests, programmers generally need to develop Handler according to business needs.
4.HandlAdapter: Processor adapter. The processor is executed through HandlerAdapter, which is an application of the adapter pattern. By extending the adapter, more types of processors can be executed. Implemented by the framework.
5.ModelAndView is the encapsulation object of springmvc, which encapsulates the model and view together.
6.ViewResolver: View resolver. ViewResolver is responsible for generating the View view from the processing results. ViewResolver first parses the logical view name into the physical view name, which is the specific page address, and then generates the View view object. Finally, it renders the View and displays the processing results to the user through the page.
7View: It is the encapsulation object of springmvc and an interface. The springmvc framework provides many View types, including: jspview, pdfview, jstlView, freemarkerView, pdfView, etc. Generally, model data needs to be displayed to users through page tags or page template technology, and programmers need to develop specific pages based on business needs.


Code corresponding to the execution process
1. When the request reaches the first stop of the front-end controller, do some preparation work first.

/**
 * 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 {<!-- -->
   if (logger.isDebugEnabled()) {<!-- -->
      String requestUri = urlPathHelper.getRequestUri(request);
      logger.debug("DispatcherServlet with name '" + getServletName() + "' processing " + request.getMethod() +
            " request for [" + requestUri + "]");
   }

    //Protect the scene
   // 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)) {<!-- -->
      logger.debug("Taking snapshot of request attributes before include");
      attributesSnapshot = new HashMap<String, Object>();
      Enumeration<?> attrNames = request.getAttributeNames();
      while (attrNames.hasMoreElements()) {<!-- -->
         String attrName = (String) attrNames.nextElement();
         if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {<!-- -->
            attributesSnapshot.put(attrName, request.getAttribute(attrName));
         }
      }
   }

    //Storage frame-related information to request for later use by processors and views
   // 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());

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

    //Request distribution
   try {<!-- -->
      doDispatch(request, response);
   }
   finally {<!-- -->
      // Restore the original attribute snapshot, in case of an include.
      if (attributesSnapshot != null) {<!-- -->
         restoreAttributesAfterInclude(request, attributesSnapshot);
      }
   }
}

2. Start processing the request
//Find the closest key (url) in the HandlerMap through the url, and then obtain the HandlerMapping object from the key
//Get the processor through the processor mapper;
//Obtained through query processor adapter

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {<!-- -->
   HttpServletRequest processedRequest = request;
   HandlerExecutionChain mappedHandler = null;
   int interceptorIndex = -1;

   try {<!-- -->
      ModelAndView mv;
      boolean errorView = false;

      try {<!-- -->
         processedRequest = checkMultipart(request);

         // Determine handler for the current request
        //Steps 3.1~3.4 are used to obtain the processor execution chain HandlerExecutionChain containing the processor Handler and interceptor AdapterIntercepters.
         mappedHandler = getHandler(processedRequest, false);
         if (mappedHandler == null || mappedHandler.getHandler() == null) {<!-- -->
            noHandlerFound(processedRequest, response);
            return;
         }

         // Determine handler adapter for the current request.
        //Steps 4.1~4.2, obtain the processor adapter according to the processor Handler in HandlerExecutionChain
         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)) {<!-- -->
            long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
            if (logger.isDebugEnabled()) {<!-- -->
               String requestUri = urlPathHelper.getRequestUri(request);
               logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
            }
            if (new ServletWebRequest(request, response).checkNotModified(lastModified) & amp; & amp; isGet) {<!-- -->
               return;
            }
         }

         // Apply preHandle methods of registered interceptors.
         HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
         if (interceptors != null) {<!-- -->
            for (int i = 0; i < interceptors.length; i + + ) {<!-- -->
               HandlerInterceptor interceptor = interceptors[i];
               if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {<!-- -->
                  triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
                  return;
               }
               interceptorIndex = i;
            }
         }

         // Actually invoke the handler.
        //5.1~5.3 Call the processor through the processor adapter HandlerApapter to complete the processing of the request
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

         // Do we need view name translation?
         if (mv != null & amp; & amp; !mv.hasView()) {<!-- -->
            mv.setViewName(getDefaultViewName(request));
         }

         // Apply postHandle methods of registered interceptors.
         if (interceptors != null) {<!-- -->
            for (int i = interceptors.length - 1; i >= 0; i--) {<!-- -->
               HandlerInterceptor interceptor = interceptors[i];
               interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
            }
         }
      }
      catch (ModelAndViewDefiningException ex) {<!-- -->
         logger.debug("ModelAndViewDefiningException encountered", ex);
         mv = ex.getModelAndView();
      }
      catch (Exception ex) {<!-- -->
         Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
         mv = processHandlerException(processedRequest, response, handler, ex);
         errorView = (mv != null);
      }

      // Did the handler return a view to render?
      if (mv != null & amp; & amp; !mv.wasCleared()) {<!-- -->
         render(mv, processedRequest, response);
         if (errorView) {<!-- -->
            WebUtils.clearErrorRequestAttributes(request);
         }
      }
      else {<!-- -->
         if (logger.isDebugEnabled()) {<!-- -->
            logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
                  "': assuming HandlerAdapter completed request handling");
         }
      }

      // Trigger after-completion for successful outcome.
      triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
   }

   catch (Exception ex) {<!-- -->
      // Trigger after-completion for thrown exception.
      triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
      throw ex;
   }
   catch (Error err) {<!-- -->
      ServletException ex = new NestedServletException("Handler processing failed", err);
      // Trigger after-completion for thrown exception.
      triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
      throw ex;
   }

   finally {<!-- -->
      // Clean up any resources used by a multipart request.
      if (processedRequest != request) {<!-- -->
         cleanupMultipart(processedRequest);
      }
   }
}

3.1 getHandler(HttpServletRequest request), obtain HandlerExecutionChain (processor and interceptor) via HandlerMapping object

/**
 * Return the HandlerExecutionChain for this request.
 * <p>Tries all handler mappings in order.
 * @param request current HTTP request
 * @return the HandlerExecutionChain, or <code>null</code> if no handler could be found
 */
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {<!-- -->
   for (HandlerMapping hm : this.handlerMappings) {<!-- -->
      if (logger.isTraceEnabled()) {<!-- -->
         logger.trace(
               "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
      }
      HandlerExecutionChain handler = hm.getHandler(request);
      if (handler != null) {<!-- -->
         return handler;
      }
   }
   return null;
}

3.2.1 getHandler(HttpServletRequest request), obtain the processor through request, obtain the processor Handler, then obtain the interceptor, and finally form the HandlerExecutionChain

/**
 * Look up a handler for the given request, falling back to the default
 * handler if no specific one is found.
 * @param request current HTTP request
 * @return the corresponding handler instance, or the default handler
 * @see #getHandlerInternal
 */
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {<!-- -->
   Object handler = getHandlerInternal(request);
   if (handler == null) {<!-- -->
      handler = getDefaultHandler();
   }
   if (handler == null) {<!-- -->
      return null;
   }
   // Bean name or resolved handler?
   if (handler instanceof String) {<!-- -->
      String handlerName = (String) handler;
      handler = getApplicationContext().getBean(handlerName);
   }
   return getHandlerExecutionChain(handler, request);
}

3.2.2 Obtain HandlerExecutionChain containing Handler and AdaptedInterceptors based on the found processor Handler and request

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {<!-- -->
   HandlerExecutionChain chain =
      (handler instanceof HandlerExecutionChain) ?
         (HandlerExecutionChain) handler : new HandlerExecutionChain(handler);

   chain.addInterceptors(getAdaptedInterceptors());

   String lookupPath = urlPathHelper.getLookupPathForRequest(request);
   for (MappedInterceptor mappedInterceptor : mappedInterceptors) {<!-- -->
      if (mappedInterceptor.matches(lookupPath, pathMatcher)) {<!-- -->
         chain.addInterceptor(mappedInterceptor.getInterceptor());
      }
   }

   return chain;
}
/**
 * Return the adapted interceptors as HandlerInterceptor array.
 * @return the array of HandlerInterceptors, or <code>null</code> if none
 */
protected final HandlerInterceptor[] getAdaptedInterceptors() {<!-- -->
   int count = adaptedInterceptors.size();
   return (count > 0) ? adaptedInterceptors.toArray(new HandlerInterceptor[count]) : null;
}

3.3.getHandlerInternal(HttpServletRequest request) gets Handler

/**
 * Look up a handler for the URL path of the given request.
 * @param request current HTTP request
 * @return the handler instance, or <code>null</code> if none found
 */
@Override
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {<!-- -->
   String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
   Object handler = lookupHandler(lookupPath, request);
   if (handler == null) {<!-- -->
      // We need to care for the default handler directly, since we need to
      // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
      Object rawHandler = null;
      if ("/".equals(lookupPath)) {<!-- -->
         rawHandler = getRootHandler();
      }
      if (rawHandler == null) {<!-- -->
         rawHandler = getDefaultHandler();
      }
      if (rawHandler != null) {<!-- -->
         // Bean name or resolved handler?
         if (rawHandler instanceof String) {<!-- -->
            String handlerName = (String) rawHandler;
            rawHandler = getApplicationContext().getBean(handlerName);
         }
         validateHandler(rawHandler, request);
         handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
      }
   }
   if (handler != null & amp; & amp; logger.isDebugEnabled()) {<!-- -->
      logger.debug("Mapping [" + lookupPath + "] to " + handler);
   }
   else if (handler == null & amp; & amp; logger.isTraceEnabled()) {<!-- -->
      logger.trace("No handler mapping found for [" + lookupPath + "]");
   }
   return handler;
}

3.4 lookupHandler(lookupPath, request) obtains the Handler based on the given url path and request

protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {<!-- -->
   // Direct match?
   Object handler = this.handlerMap.get(urlPath);
   if (handler != null) {<!-- -->
      // Bean name or resolved handler?
      if (handler instanceof String) {<!-- -->
         String handlerName = (String) handler;
         handler = getApplicationContext().getBean(handlerName);
      }
      validateHandler(handler, request);
      return buildPathExposingHandler(handler, urlPath, urlPath, null);
   }
   // Pattern match?
   List<String> matchingPatterns = new ArrayList<String>();
   for (String registeredPattern : this.handlerMap.keySet()) {<!-- -->
      if (getPathMatcher().match(registeredPattern, urlPath)) {<!-- -->
         matchingPatterns.add(registeredPattern);
      }
   }
   String bestPatternMatch = null;
   Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
   if (!matchingPatterns.isEmpty()) {<!-- -->
      Collections.sort(matchingPatterns, patternComparator);
      if (logger.isDebugEnabled()) {<!-- -->
         logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
      }
      bestPatternMatch = matchingPatterns.get(0);
   }
   if (bestPatternMatch != null) {<!-- -->
      handler = this.handlerMap.get(bestPatternMatch);
      // Bean name or resolved handler?
      if (handler instanceof String) {<!-- -->
         String handlerName = (String) handler;
         handler = getApplicationContext().getBean(handlerName);
      }
      validateHandler(handler, request);
      String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);

      // There might be multiple 'best patterns', let's make sure we have the correct URI template variables
      // for all of them
      Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
      for (String matchingPattern : matchingPatterns) {<!-- -->
         if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {<!-- -->
            uriTemplateVariables
                  .putAll(getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath));
         }
      }
      if (logger.isDebugEnabled()) {<!-- -->
         logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
      }
      return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
   }
   // No handler found...
   return null;
}

4.1 HandlerAdapter getHandlerAdapter(Object handler), get HandlerAdapter according to Handler

/**
 * Return the HandlerAdapter for this handler object.
 * @param handler the handler object to find an adapter for
 * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
 */
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {<!-- -->
   for (HandlerAdapter ha : this.handlerAdapters) {<!-- -->
      if (logger.isTraceEnabled()) {<!-- -->
         logger.trace("Testing handler adapter [" + ha + "]");
      }
      if (ha.supports(handler)) {<!-- -->
         return ha;
      }
   }
   throw new ServletException("No adapter for handler [" + handler +
         "]: Does your handler implement a supported interface like Controller?");
}

4.2 supports(Object handler)

public boolean supports(Object handler) {<!-- -->
   return (handler instanceof Controller);
}

5.1 Use the processor to complete the processing of the request

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {<!-- -->

   ((Servlet) handler).service(request, response);
   return null;
}
public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException
{<!-- -->
    HttpServletRequest request;
    HttpServletResponse response;

    if (!(req instanceof HttpServletRequest & amp; & amp;
            res instanceof HttpServletResponse)) {<!-- -->
        throw new ServletException("non-HTTP request or response");
    }

    request = (HttpServletRequest) req;
    response = (HttpServletResponse) res;

    service(request, response);
}

5.2 service(HttpServletRequest req, HttpServletResponse resp)

protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
{<!-- -->
    String method = req.getMethod();

    if (method.equals(METHOD_GET)) {<!-- -->
        long lastModified = getLastModified(req);
        if (lastModified == -1) {<!-- -->
            // servlet doesn't support if-modified-since, no reason
            // to go through further expensive logic
            doGet(req, resp);
        } else {<!-- -->
            long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
            if (ifModifiedSince < lastModified) {<!-- -->
                // If the servlet mod time is later, call doGet()
                // Round down to the nearest second for a proper compare
                // A ifModifiedSince of -1 will always be less
                maybeSetLastModified(resp, lastModified);
                doGet(req, resp);
            } else {<!-- -->
                resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            }
        }

    } else if (method.equals(METHOD_HEAD)) {<!-- -->
        long lastModified = getLastModified(req);
        maybeSetLastModified(resp, lastModified);
        doHead(req, resp);

    } else if (method.equals(METHOD_POST)) {<!-- -->
        doPost(req, resp);

    } else if (method.equals(METHOD_PUT)) {<!-- -->
        doPut(req, resp);

    } else if (method.equals(METHOD_DELETE)) {<!-- -->
        doDelete(req, resp);

    } else if (method.equals(METHOD_OPTIONS)) {<!-- -->
        doOptions(req,resp);

    } else if (method.equals(METHOD_TRACE)) {<!-- -->
        doTrace(req,resp);

    } else {<!-- -->
        //
        // Note that this means NO servlet supports whatever
        // method was requested, anywhere on this server.
        //

        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[1];
        errArgs[0] = method;
        errMsg = MessageFormat.format(errMsg, errArgs);

        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
    }
}

5.3 doGet(HttpServletRequest req, HttpServletResponse resp)

protected void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
{<!-- -->
    String protocol = req.getProtocol();
    String msg = lStrings.getString("http.method_get_not_supported");
    if (protocol.endsWith("1.1")) {<!-- -->
        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
    } else {<!-- -->
        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
    }
}