Spring MVC Source Analysis

I. Analysis of Processing Processes

1. First of all, Tomcat loads and parses the / WEB-INF/web.xml file every time it starts up, so you can find a breakthrough from web.xml. The main code is as follows:

<servlet >
    <servlet-name >spring-mvc</servlet-name>
    <!-- servlet class -->
    <servlet-class >org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- Initialization parameters -->
    <init-param >
      <param-name >contextConfigLocation</param-name>
      <param-value >classpath:/spring-mvc.xml</param-value>
    </init-param>
    <!-- Start-up loading -->
    <load-on-startup >1</load-on-startup>
  </servlet>
  <servlet-mapping >
    <servlet-name >spring-mvc</servlet-name>
    <url-pattern >/</url-pattern>
  </servlet-mapping>
   .
   .
   .
</servlet >

We can get three information from the web.xml file: the servlet class is Dispatcher Servlet, which loads at startup and initializes the parameter contextConfigLocation at load.
For the spring-mvc.xml file address under classpath, next we move our eyes to the Dispatcher Servlet class.

2. Opening the Dispatcher Servlet class, we first focus on its architecture, as follows:


Obviously, it's a subclass of Servlet, which needless to say, because web.xml is already configured. Since it is a Servlet, we should focus on its service, doGet, doPost and other related methods. In its parent FrameServlet, we found the service method.

/**
     * Override the parent class implementation in order to intercept PATCH
     * requests.
     */
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String method = request.getMethod();
        if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
            processRequest(request, response);
        }
        else {
            super.service(request, response);
        }
    }

According to the service method, we find a method chain service -> process Request -> doService -> doDispatch step by step, and we will eventually focus on doDispatch, which is the core method of the whole Spring MVC.

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 {
                //Processing file upload requests
                processedRequest = checkMultipart(request);
                multipartRequestParsed = processedRequest != request;

                // Parse the request to get the HandlerExecutionChain object
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Getting the HandlerAdapter object from the HandlerExecutionChain object is actually getting it from the HandlerMapping object
                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()) {
                        logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                    }
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                //Before the controller method is executed, the interceptor-related method (pre) is executed.
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                try {
                    // Execute the handler method of the HandlerAdapter object and return ModelAndView
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                }
                finally {
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }
                }

                applyDefaultViewName(request, mv);
                //After the controller method is executed, the interceptor's related method (post) is executed.
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            //View parsing
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Error err) {
            triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
        }
        finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                return;
            }
            // Clean up any resources used by a multipart request.
            if (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }

It's not too hard to say that it's the core. As can be seen from the Chinese annotations of the above code, it includes parsing requests, executing relevant interceptors, and executing handle methods. Don't worry. Next we'll talk about it. Anyway, it's very important. Execute the view parsing method.  
3. What is the Handler Adapter for? What's the use of its handler method? We don't have a concept, and then we start from another perspective (just like two people walking opposite each other, one person is exhausted, and only one person can meet by working hard on the other), so I choose Controller, and I have to start with the configuration file, because it uses spring's IOC.

<bean id="controller" class="com.mvc.controller.MyController"></bean>

    <bean id="interceptor" class="com.mvc.interceptor.MyInterceptor"></bean>

    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="controller">controller</prop>
            </props>
        </property>
        <property name="interceptors">
            <array>
                <ref bean="interceptor"></ref>
            </array>
        </property>
    </bean>

Configuration files also give us an important message: both controllers and interceptors are passed in as parameters of SimpleUrlHandler Mapping, which is a subclass of Handler Mapping. From here, you can guess that the core method of controller is either directly invoked by Handler Mapping or invoked by the ancillary product (class) of Handler Mapping. Next, let's look at the invocation of the core method of controller.


Fortunately, seeing SimpleController Handler Adapter and Dispatcher Servlet. do Dispatch (request, response), I seem to have discovered the New World. Isn't that what we want? Handler Adapter classes and doDispatch(request, response) methods are perfectly integrated. Look at SimpleController Handler Adapter's handler method:

@Override
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        return ((Controller) handler).handleRequest(request, response);
    }

There is also a call chain for methods. As can be seen from the figure above, the handle method ultimately calls the handleRequestInternal method, which is the method we customize in the controller. In a word, handler method of Handler Adapter is used to call handleRequestInternal method in controller, and handleRequestInternal method body is our user-defined business logic.  
Okay, we have parsed the main source code of Spring MVC, and then we will summarize the process of Spring MVC.

Summary of Spring MVC Processing Process
Put a picture first, and then we will analyze it slowly.

Process analysis:
1. When the request arrives, the Dispatcher Servlet captures the request, executes the doService method, and then executes the doDispatch method.  
2. Handler Mapping parses the request and returns Handler Execution Chain (which contains controllers and interceptors), then gets Handler related classes through Handler Execution Chain, and gets Handler Adapter classes to execute it according to Handler.  
3, first implement the pre correlation method of the interceptor, then execute the handler method, it will call the handleRequestInternal method of controller (the method body is defined by the user), and finally call the interceptor's post correlation method.  
4. Parse the ModelAndView returned by the handler method (you can configure ResourceViewResolver in the configuration file, which is the view parser), render the page and respond to the client.

Tags: Programming Spring xml Tomcat

Posted on Thu, 10 Oct 2019 23:51:20 -0700 by the_ut_tick