Spring MVC series execution process

Article directory

This series combines the source code (based on springboot2.1.4) to analyze the execution process of springmvc.

First, a complete flow chart:

As we know, the request initiated by the front-end goes to tomcat first, and the servlet is finally executed. springmvc implements a servlet called dispatcher servlet, and its inheritance relationship is as follows:

Suppose a get request is initiated, then the doGet method in the servlet will be executed, and finally to the dispatcher servlet. The link is as follows:
FrameworkServlet#doGet(HttpServletRequest request, HttpServletResponse response)
 -->FrameworkServlet#processRequest(HttpServletRequest request, HttpServletResponse response)
  -->DispatcherServlet#doService(HttpServletRequest request, HttpServletResponse response)
    -->DispatcherServlet#doDispatch(HttpServletRequest request, HttpServletResponse response)

Finally, it enters the doDispatch method of the DispatcherServlet class.

doDispatch() method

This method is the core method of sprngmvc. All the logic to turn the tide is here:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	HttpServletRequest processedRequest = request;
	//Processor execution chain, including interceptors and specific controller s
	HandlerExecutionChain mappedHandler = null;
	boolean multipartRequestParsed = false;
	WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
	try {
		ModelAndView mv = null;
		Exception dispatchException = null;

		try {
			//Check whether there is a file upload type
			processedRequest = checkMultipart(request);
			multipartRequestParsed = (processedRequest != request);

			// Determine handler for the current request.
			//Determine the mappedHandler to execute, that is, the controller to execute
			mappedHandler = getHandler(processedRequest);
			if (mappedHandler == null) {
				noHandlerFound(processedRequest, response);
				return;
			}
			// Determine handler adapter for the current request.
			//Get handler adapter, there are 3 kinds in total
			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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
					return;
				}
			}
			//preHandle method of executing interceptor
			if (!mappedHandler.applyPreHandle(processedRequest, response)) {
				return;
			}
			// Actually invoke the handler.
			//Execute the controller to get the model and view
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
			if (asyncManager.isConcurrentHandlingStarted()) {
				return;
			}
			//If there is no view name, set a default name
			applyDefaultViewName(processedRequest, mv);
			//The postHandle method of executing interceptor
			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);
		}
		//Render the view and set the jump path to response
		//Then execute the interceptor's afterCompletion method
		processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
	}
	catch (Exception ex) {
		//To throw an exception, the interceptor's afterCompletion method should also be executed
		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);
			}
		}
	}
}

Main steps:
1. Confirm handlerMapping, and get the execution chain of HandlerExecutionChain, which includes interceptor and controller method path;
2. Determine the handler adapter. There are three types in total:
3. Use the adapter to execute the handler, that is, the controller, but if there is an interceptor, first execute the interceptor related methods.
4. Render the view and return to

The above is the general request process of spring MVC, and the following is a detailed analysis of specific methods.

getHandler(HttpServletRequest request)

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	if (this.handlerMappings != null) {
		for (HandlerMapping mapping : this.handlerMappings) {
			HandlerExecutionChain handler = mapping.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
	}
	return null;
}

The logic of the method is relatively simple, that is, traversal
Here, handlerMappings is a list. By default, there are 5 instance objects, 4 types in total:

  • SimpleUrlHandlerMapping
  • RequestMappingHandlerMapping, processing the @ RequestMapping decorated method in the @ Controller decorated class
  • BeanNameUrlHandlerMapping
  • Simpleurhandlermapping, the same type as the first, but different instance object
  • Welcome pagehandlermapping, the default application welcome page

Traversing and invoking the mapping.getHandler(request) method to get HandlerExecutionChain.

The getHandler method is implemented in the parent class AbstractHandlerMapping:

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 = obtainApplicationContext().getBean(handlerName);
	}

	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

	if (logger.isTraceEnabled()) {
		logger.trace("Mapped to " + handler);
	}
	else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
		logger.debug("Mapped to " + executionChain.getHandler());
	}

	if (CorsUtils.isCorsRequest(request)) {
		CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
		CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
		CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
	}

	return executionChain;
}

getHandlerAdapter(Object handler)

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
	if (this.handlerAdapters != null) {
		for (HandlerAdapter adapter : this.handlerAdapters) {
			if (adapter.supports(handler)) {
				return adapter;
			}
		}
	}
	throw new ServletException("No adapter for handler [" + handler +
			"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
  • RequestMappingHandlerAdapter
  • HttpRequestHandlerAdapter
  • SimpleControllerHandlerAdapter

processDispatchResult()

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

		// Did the handler return a view to render?
		if (mv != null && !mv.wasCleared()) {
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			if (logger.isTraceEnabled()) {
				logger.trace("No view rendering, null ModelAndView returned.");
			}
		}

		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Concurrent handling started during a forward
			return;
		}

		if (mappedHandler != null) {
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}
44 original articles published, 20 praised, 9128 visited
Private letter follow

Tags: Tomcat Spring

Posted on Mon, 13 Jan 2020 03:39:08 -0500 by Dark-Elk