SpringMVC source code analysis-key source code analysis

Premise points
Benpian articles used for the project SpringMVC source code analysis basics ------ (b) of the Code

1. The important DispatcherServlet
DispatcherServlet is the core class of SpringMVC, commonly known as the front controller.
All client requests will be forwarded to it for execution, and finally we can execute our url request (find the request method according to the url, and execute it by reflection)

2. Key source code analysis
We understand that DispatcherServlet is a front-end controller, so our front-end requests will definitely pass his processing. Let's take a look at the DispatcherServlet class.
Looking at the class diagram (ctrl+alt+u),
Insert picture description hereyou can see that its parent class is FrameworkServlet, and the parent class of FrameworkServlet is HttpServlet. In Java, the operation of the subclass needs to execute the method of the parent first, so the method of HttpServlet is executed first. . See the service() method of HttpServlet.

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

What we defined in the project are all GET methods, so this method executes to the doGet(req, resp) method.
Insert picture description hereEnter the doGet method of the subclass FrameworkServlet and
Insert picture description hereenter this method, you can see that
Insert picture description herethis doService() is the core method. Enter and have a look (after entering, we will find that we have come to the front controller, which is DispatcherServlet)
Insert picture description here
and then jump to doDispatch (request, response), enter this method.

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.
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
    
    
					noHandlerFound(processedRequest, response);
					return;
				}

				// 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)) {
    
    
					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;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    
    
					return;
				}

				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
    
    
					return;
				}

				applyDefaultViewName(processedRequest, mv);
				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);
			}
			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(processedRequest, response);
				}
			}
			else {
    
    
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
    
    
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

Now we take a look at this method doDispatch(HttpServletRequest request, HttpServletResponse response).

  1. processedRequest = checkMultipart(request);Determine whether it is a file upload
  2. mappedHandler = getHandler(processedRequest);
    Enter the getHandler() method to view.
    Insert picture description hereWe can view the HandlerMapping in the for loop through debug.
    Insert picture description hereThere are five in total, we just need to understand the first two. We mainly look at the first one. Check the data
    this.handlerMapping[0] data, Insert picture description hereyou can see that there are interceptors we defined, as well as url mapping. At this time we know that HandlerMapping stores all the information of mvc. The data of this.handlerMapping[1] is similar, he just collects xml startup configuration information. And the this.handlerMapping[0] data is the configuration information that is started by collecting annotations.
    After seeing the
    Insert picture description hereentry,
    Insert picture description hereenter the
    Insert picture description here
    Insert picture description herelookupPath variable to store the visited url.
    We are back to DispatcherServlet.
  3. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());寻找适配器Insert picture description hereInsert picture description hereThis method mainly judges whether it is a subclass of HandlerMethod, and the latter judgement of supportsInternal((HandlerMethod) handler) returns true when handler instanceof HandlerMethod is true.
  4. if (!mappedHandler.applyPreHandle(processedRequest, response))
    PreHandle should be familiar. Isn't this the first method of our interceptor?
    Enter to see the code.
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
    
		// 获取所有的拦截器
		HandlerInterceptor[] interceptors = getInterceptors();
		// 拦截器判空
		if (!ObjectUtils.isEmpty(interceptors)) {
    
    
			// 遍历所有拦截器
			for (int i = 0; i < interceptors.length; i++) {
    
    
				HandlerInterceptor interceptor = interceptors[i];
				// 调用拦截器的preHandle()方法
				if (!interceptor.preHandle(request, response, this.handler)) {
    
    
					triggerAfterCompletion(request, response, null);
					return false;
				}
				this.interceptorIndex = i;
			}
		}
		return true;
	}
  1. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    Insert picture description hereInsert picture description hereThere are several important statements to
    if (this.synchronizeOnSession)determine whether to use the session
    invokeHandlerMethod(request, response, handlerMethod);execution target method
  2. mappedHandler.applyPostHandle(processedRequest, response, mv);The same as the fourth point, the PostHandle() method of the execution interceptor, the internal method is the same, so I won’t look at it.
  3. processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    Enter the viewing method. The method of
    Insert picture description hererender(mv, request, response);setting the rendering view
    mappedHandler.triggerAfterCompletion(request, response, null);is clicked to enter the view~~ (actually, if you look at this AfterCompletion familiar, you should know what it is)~~
    Insert picture description hereIt is easy to understand, this is the call of the interceptor's AfterCompletion() method, which also explains, we The question mentioned in the basic knowledge (2), why two are after the execution of the target method. In fact, the difference between them is that one (PreHandle) is set before rendering, and the other (AfterCompletion) is set after rendering.

Guess you like

Origin blog.csdn.net/weixin_43911969/article/details/114685881