SpringMvc源码分析2——DispatcherServlet


从SpringMvc的中心处理器DispatcherServlet开始说起。源码主要是基于jdk1.8,springboot 2.1.6.RELEASE,添加一个spring-boot-starter-web依赖。

一. DispatcherServlet结构图

DispatcherServlet

二. 测试代码

首先我们先暴露一个接口

@RestController
public class IndexController {
    @GetMapping("/hello")
    public String hello(String name) {
        return "hello: " + name;
    }
}

调用http://localhost:8080/hello:

1. 调用链

Debug一下: 查看一下调用链,如下:

Xnip2020-02-24_15-02-06

1. HttpServlet
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException

2. FrameworkServlet
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException

3. HttpServlet
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException

4. FrameworkServlet
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
  
5. DispatcherServlet
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception

6. DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception

我们查看一下具体的代码,

2. FrameworkServlet#processRequest

代码进行了一些删减

/**
 * 处理此请求,不考虑结果而发布事件,实际事件处理由抽象执行
 */
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

  LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		LocaleContext localeContext = buildLocaleContext(request);

		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

  	// 设置了LocaleContextHolder和RequestContextHolder,使用ThreadLocal保存,所以在之后可以直接通过LocaleContextHolder,RequestContextHolder获取相关信息
		initContextHolders(request, localeContext, requestAttributes);

		try {
      // todo下面分析
			doService(request, response);
		} catch (ServletException | IOException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (Throwable ex) {
			failureCause = ex;
			throw new NestedServletException("Request processing failed", ex);
		}

		finally {
      // 重置LocaleContextHolder和RequestContextHolder
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}
			logResult(request, response, failureCause, asyncManager);
      // 发布相关事件
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

前一部分是将当前请求的Locale对象和请求的属性,通过LocaleContextHolderRequestContextHolder设置到ThreadLocal对象中,也就是分别将这两个对象和请求线程做了绑定。之后我们就可以从里面获取这些绑定的值使用。调用doService()处理结束后,再通过LocaleContextHolderRequestContextHolder恢复相应的绑定。接着发布了一个ServletRequestHandledEvent事件。具体我们再看doService()方法。

3. DispatcherServlet#doService

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		logRequest(request);

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

		// Make framework objects available to handlers and view objects.
  	// 给request设置一些属性
		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());

		if (this.flashMapManager != null) {
			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);
		}

		try {
			doDispatch(request, response);
		}
		finally {
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// Restore the original attribute snapshot, in case of an include.
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}

这里面有几个requet.setAttribute()方法的调用,将之前在初始化流程中实例化的对象设置到request的属性中,供下一步处理使用,这里有web上下文,本地化解析器等。然后继续调用doDispatch()处理请求。

4. DispatcherServlet#doDispatch

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.
        // 根据当前请求获取HandlerMapping,并得到HandlerExecutionChain
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
        // 获取匹配的HandlerAdapter
				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()方法
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
        // 调用handler,得到ModelAndView
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

				applyDefaultViewName(processedRequest, mv);
        // 执行拦截器的 postHandle()方法
				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);
			}
      // 处理ModelAndView,最后再执行拦截器的 afterCompletion()方法
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
      // 异常,执行拦截器的 afterCompletion()方法
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
      // 异常,执行拦截器的 afterCompletion()方法
			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);
				}
			}
		}
	}

到此终于找了处理流程的主线,我们看到了一系列SpringMvc源码分析1 中介绍的对象,以及处理流程。

这里会从DispatcherServlet内维护的handlerMappings中找到匹配的HandlerMapping方法,并执行其getHandler()方法,得到HandlerExecutionChain,看一下具体的内部结构:

HandlerExecutionChain.png

可以看到维护了handler和拦截器列表。

接着HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());得到HandlerAdapter,然后执行拦截器的postHandle()方法。之后调用HandlerAdapterhandle(),处理controller里面的逻辑,得到ModelAndView,接着执行拦截器的postHandle()方法。然后就会根据viewResolvers得到具体的视图View,最后就是渲染视图,返回给客户端。

三. 总结

到此,我们可以了解DispatcherServlet大概的处理流程。其中

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());还有很多的处理逻辑,包括HandlerMethodArgumentResolver,HandlerMethodReturnValueHandler,HttpMessageConverter等的处理,我们后面继续分析。


文章作者: shiv
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 shiv !
评论
  目录