天天看点

SpringMVC源码解析之DispatcherServlet:请求处理一、FrameworkServlet二、DispatcherServlet

SpringMVC源码解析之Servlet

SpringMVC源码解析之GenericServlet和HttpServlet

SpringMVC源码解析之DispatcherServlet:生命周期init()和destroy()

一、FrameworkServlet

1. FrameworkServlet#service(HttpServletRequest, HttpServletResponse)

@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {

	HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
	if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
		processRequest(request, response);
	}
	else {
		super.service(request, response);
	}
}
           
//其它的doPost, doDelete, doPut方法也类似
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {

	processRequest(request, response);
}
           

与HttpServlet将请求按照请求方式的不同分发给doXXX方法处理不同,FrameworkServlet除了增加了PATCH请求的处理以外,还将所有的请求(TRACE和OPTIONS请求不一定)处理又集中到了processRequest方法。

至于为什么不直接覆盖service方法,而需要在doXXX一一覆盖?是因为将请求分发给doXXX是HttpServlet中的一种规范,如果是直接在service方法中覆盖sevice方法并在其中直接调用proceeRequest固然可以实现,但是这样子doXXX方法将毫无意义,如果在子类中需要针对某个特定类型的请求(如POST)做处理,无法直接通过覆盖doPost实现,而是需要覆盖service方法,不符合正常的逻辑,而且一般情况下开发者不需要对SpringMVC的内部结构和实现有足够了解。

2. FrameworkServlet#doOptions(HttpServletRequest, HttpServletResponse)

protected void doOptions(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {

	//dispatchOptionsRequest为true(默认false) || cores的预请求
	if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) {
		processRequest(request, response);
		if (response.containsHeader("Allow")) {
			// Proper OPTIONS response coming from a handler - we're done.
			return;
		}
	}

	//交给父类处理,会在父类的处理结果上加上PATCH
	// Use response wrapper in order to always add PATCH to the allowed methods
	super.doOptions(request, new HttpServletResponseWrapper(response) {
		@Override
		public void setHeader(String name, String value) {
			if ("Allow".equals(name)) {
				value = (StringUtils.hasLength(value) ? value + ", " : "") + HttpMethod.PATCH.name();
			}
			super.setHeader(name, value);
		}
	});
}
           

dispatchOptionsRequest为true(默认false)或者 cores的预请求 会进入processRequest方法,否则按照HttpServlet#doOptions方法处理,但是处理结果会加上PATCH。因为FrameworkServlet类增加了对OPTIONS请求的处理。

3. FrameworkServlet#doTrace(HttpServletRequest, HttpServletResponse)

protected void doTrace(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {

	//dispatchTraceRequest为true(默认false)
	if (this.dispatchTraceRequest) {
		processRequest(request, response);
		if ("message/http".equals(response.getContentType())) {
			// Proper TRACE response coming from a handler - we're done.
			return;
		}
	}
	super.doTrace(request, response);
}
           

dispatchTraceRequest为true时会进入 processRequest方法。

4. FrameworkServlet#processRequest(HttpServletRequest, HttpServletResponse)

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {

	long startTime = System.currentTimeMillis();
	Throwable failureCause = null;

	//获取原来的LocaleContext
	LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
	//获取当前请求的LocalContext
	LocaleContext localeContext = buildLocaleContext(request);

	//获取原来的RequestAttributes,RequestContextHolder是ThreadLocal类型
	RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
	//获取当前请求的RequestAttributes
	ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

	//异步请求的处理
	WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
	asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

	//将当前请求的LocaleContext和RequestContext设置到ThreadLocal中
	initContextHolders(request, localeContext, requestAttributes);

	try {
		//核心逻辑,模板方法
		doService(request, response);
	}
	catch (ServletException | IOException ex) {
		failureCause = ex;
		throw ex;
	}
	catch (Throwable ex) {
		failureCause = ex;
		throw new NestedServletException("Request processing failed", ex);
	}

	finally {
		//ThreadLocal中的LocaleContext和RequestAttributes恢复成原来的对象
		resetContextHolders(request, previousLocaleContext, previousAttributes);
		if (requestAttributes != null) { //标志请求完成
			requestAttributes.requestCompleted();
		}

		if (logger.isDebugEnabled()) {
			if (failureCause != null) {
				this.logger.debug("Could not complete request", failureCause);
			}
			else {
				if (asyncManager.isConcurrentHandlingStarted()) {
					logger.debug("Leaving response open for concurrent processing");
				}
				else {
					this.logger.debug("Successfully completed request");
				}
			}
		}

		//发布ServletRequestHanldedEvrnt事件
		publishRequestHandledEvent(request, response, startTime, failureCause);
	}
}
           

该方法主要做了3件事

(1)LocaleContext和RequestAttributes的管理

(2)异步请求的处理,注入WebAsyncManager

(3)处理完成后,requestAttributes.requestCompleted()标记请求完成,发布ServletRequestHanldedEvrnt事件

5. FrameworkServlet#initContextHolders(HttpServletRequest, LocaleContext, RequestAttributes) & FrameworkServlet#resetContextHolders(HttpServletRequest, LocaleContext, RequestAttributes)

//设置LocaleContext和RequestContext
private void initContextHolders(HttpServletRequest request,
		@Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {

	if (localeContext != null) {
		LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
	}
	if (requestAttributes != null) {
		RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
	}
	if (logger.isTraceEnabled()) {
		logger.trace("Bound request context to thread: " + request);
	}
}
           
//恢复LocalContext和RequestAttributes

private void resetContextHolders(HttpServletRequest request,
		@Nullable LocaleContext prevLocaleContext, @Nullable RequestAttributes previousAttributes) {

	LocaleContextHolder.setLocaleContext(prevLocaleContext, this.threadContextInheritable);
	RequestContextHolder.setRequestAttributes(previousAttributes, this.threadContextInheritable);
	if (logger.isTraceEnabled()) {
		logger.trace("Cleared thread-bound request context: " + request);
	}
}
           

LocaleContextHolder和RequestContextHolder通过LocalThread类型对属性值进行管理。

以LocaleContextHolder为例,这是一个abstract类。

private static final ThreadLocal<LocaleContext> localeContextHolder =

new NamedThreadLocal<>(“LocaleContext”);

private static final ThreadLocal inheritableLocaleContextHolder =

new NamedInheritableThreadLocal<>(“LocaleContext”);

内部有两个static final的常量,两者都是ThreadLocal的子类型,其中inheritableLocaleContextHolder可以被子线程继承。

默认由localeContextHolder维护属性LocaleContext,如果属性是可继承的,则由inheritableLocaleContextHolder进行维护。

RequestContextHolder的实现方式和LocaleContextHolder基本一致。

LocaleContext对Locale实例进行管理,储存的是本地化信息(如zh-cn)

RequestAttributes可以对Request和Session的attribute进行管理(get/set/remove),根据scope判断操作的是Request还是Session。

6. ServletRequestAttributes#requestCompleted()

public void requestCompleted() {
	executeRequestDestructionCallbacks();
	updateAccessedSessionAttributes();
	this.requestActive = false;
}
           

(1)执行所有请求完成的回调,请求完成的回调线程类保存在requestDestructionCallbacks中,完成后进行清空

private void executeRequestDestructionCallbacks() {
	synchronized (this.requestDestructionCallbacks) {
		for (Runnable runnable : this.requestDestructionCallbacks.values()) {
			runnable.run();
		}
		this.requestDestructionCallbacks.clear();
	}
}
           

(2)更新session属性

protected void updateAccessedSessionAttributes() {
	if (!this.sessionAttributesToUpdate.isEmpty()) {
		// Update all affected session attributes.
		HttpSession session = getSession(false);
		if (session != null) {
			try {
				for (Map.Entry<String, Object> entry : this.sessionAttributesToUpdate.entrySet()) {
					String name = entry.getKey();
					Object newValue = entry.getValue();
					Object oldValue = session.getAttribute(name);
					if (oldValue == newValue && !isImmutableSessionAttribute(name, newValue)) {
						session.setAttribute(name, newValue);
					}
				}
			}
			catch (IllegalStateException ex) {
				// Session invalidated - shouldn't usually happen.
			}
		}
		this.sessionAttributesToUpdate.clear();
	}
}
           

(3)修改标志变量requestActive

7. FrameworkServlet#publishRequestHandledEvent(HttpServletRequest, HttpServletResponse, long, Throwable)

//发布请求已经处理的事件ServletRequestHanldedEvrnt
private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response,
		long startTime, @Nullable Throwable failureCause) {

	if (this.publishEvents && this.webApplicationContext != null) {
		// Whether or not we succeeded, publish an event.
		long processingTime = System.currentTimeMillis() - startTime;
		this.webApplicationContext.publishEvent(
				new ServletRequestHandledEvent(this,
						request.getRequestURI(), request.getRemoteAddr(),
						request.getMethod(), getServletConfig().getServletName(),
						WebUtils.getSessionId(request), getUsernameForRequest(request),
						processingTime, failureCause, response.getStatus()));
	}
}
           

二、DispatcherServlet

1. 组件

前面的文章已经提到,在DispatcherServlet#onRefresh()方法中初始化9个策略组件,这些组件会在请求处理时发挥作用,下面先简单提一下每个组件的作用。

(1)MultipartResolver

multipart解析器,处理文件上传。

如果在配置了MultipartResolver,每个请求都会被检查是否包含multipart。如果包含,会交给MultipartResolver进行解析。

(2)LocaleResolver

本地化信息的解析器,指定本地化信息Locale。

(3)ThemeResolver

主题资源解析器,指定主题Theme。Web开发中一般通过主题控制网页风格,一个主题就是一组静态,如换肤功能。

(4)HandlerMapping & HandlerAdapter

处理器映射

SpringMVC将请求的具体处理逻辑称为处理器handler,处理器可以是一个类,也可以是一个方法,也可以是其它方式,因此处理器handler的类型为Object.

为了保证同意调用,SpringMVC提供了一个适配接口HandlerAdapter,对handler进行封装。

HandlerMapping称为处理器映射,根据请求找到对应的HandlerExecutionChain(包括具体的处理器Handler和拦截器集Interceptors)

(5)HandlerExceptionResolver

处理异常解析器,在请求处理过程中出现异常时进行处理。

(6)ViewResolver

视图解析器,根据逻辑视图(视图名)到物理视图

(7)RequestToViewNameTranslator

在Handler没有设置视图时,从请求中获取逻辑视图

(8)FlashMapManager

对FlashMap进行管理,FlashMap可以在redirect转发时传递参数

2. DispatcherServlet#doService(HttpServletRequest, HttpServletResponse)

/**
 * 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 resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
		logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
				" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
	}

	//对于include请求,保存一份请求属性的快照
	// 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));
			}
		}
	}

	//设置请求属性,后续handler和view中需要进行使用
	// 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());

	//用于请求redirect转发,FlashMap用于转发时传递参数,否则只能在url中携带参数
	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) {
				//还原include请求的快照属性
				restoreAttributesAfterInclude(request, attributesSnapshot);
			}
		}
	}
}
           

(1)属性设置,用于后续handler和view以及redirect转发的处理

(2)include请求在处理前对属性做快照,处理后进行恢复

(3)具体的请求处理方法交给doDispatcher处理

对于FlashMapManager,用于管理请求转发时携带属性。

正常情况下,direct是没有携带参数的功能的,如果需要携带参数,只能加在url,但是url有长度限制而且是显式的。

SpringMVC提供了FlashMap用来在direct时携带参数,具体的方式如下:

((FlashMap((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest().getAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE)).put(“key”, “value”);

SpringMVC本身也提供了对应功能的封装:

RedirectAttributes#addFlashAttribute:参数保存在FlashMap中。

RedirectAttributes#addAttribute:参数拼接到url中。

3. DispatcherServlet#doDispatcher(HttpServletRequest, HttpServletResponse)

/**
 * Process the actual dispatching to the handler.
 * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
 * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
 * to find the first that supports the handler class.
 * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
 * themselves to decide which methods are acceptable.
 * @param request current HTTP request
 * @param response current HTTP response
 * @throws Exception in case of any kind of processing failure
 */
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 {
			//检查并处理multipart请求,如果是multipart请求会处理成MultipartHttpServletRequest返回
			processedRequest = checkMultipart(request);
			//是否是multipart请求
			multipartRequestParsed = (processedRequest != request);

			//根据HandlerMapping找到对应的HandlerExecutionChain
			// Determine handler for the current request.
			mappedHandler = getHandler(processedRequest);
			if (mappedHandler == null) {
				//没有找到handler时的处理
				noHandlerFound(processedRequest, response);
				return;
			}

			//获取对应的HandlerAdapter
			// 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);
			//GET || HEAD请求 的Last-Modified缓存
			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;
				}
			}

			//执行Interceptor集的preHandle
			if (!mappedHandler.applyPreHandle(processedRequest, response)) {
				return;
			}

			// Actually invoke the handler.
			//执行HanlderAdapter的请求处理逻辑
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

			//异步处理请求
			if (asyncManager.isConcurrentHandlingStarted()) {
				return;
			}

			//没有设置view时提供默认view进行设置
			applyDefaultViewName(processedRequest, mv);
			//执行Interceptor集的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);
		}
		//处理返回结果和异常
		processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
	}
	//针对processDispatchResult方法捕捉其中抛出的Exception和Error
	catch (Exception ex) {
		//执行Interceptor集的afterCompletion
		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 {
			//multipart请求清理,将上传的文件删除
			// Clean up any resources used by a multipart request.
			if (multipartRequestParsed) {
				cleanupMultipart(processedRequest);
			}
		}
	}
}
           
  1. 检查是否是multipart请求,如果是则将请求封装成上传类型的请求MultipartHttpServletRequest,并置标记multipartRequestParsed为true。

    使用了组件MultipartResolver

  2. 获取handler处理器链(HandlerExecutionChain),包含了与当前请求对应的处理器Handler和拦截器Interceptor集。

    使用了HandlerMapping

  3. 处理Get和Head请求的缓存问题(Last-Modified),如果未过期直接返回。
  4. 调用Interceptor集的preHanlde。
  5. 由HandlerAdapter处理请求(controller层方法的执行入口)。

    使用了HandlerAdapter

  6. 检查请求是否是异步的。如果是异步请求直接返回。
  7. 如果ModelAndView中没有设置view,则采取默认view进行设置。
  8. 调用Interceptor集的postHandler。
  9. 调用processDiapatchResult对执行结果或异常进行处理。即使执行过程抛出异常,也会在catch语句中捕捉并调用Interceptor的afterCompletion方法。

    使用了组件LocaleResolver, ViewResolver和ThemeResolver(view#render)

  10. finally语句块进行收尾,异步请求回调MapperHandler的applyAfterConcurrentHandlingStarted方法,multipart请求删除上传资源。

4. DispatcherServlet#processDispatcherResult(HttpServletRequest, HttpServletResponse, HandlerExecutionChain, ModelAndView, Exception)

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
		@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
		@Nullable Exception exception) throws Exception {

	boolean errorView = false;

	//异常处理
	if (exception != null) {
		//ModelAndViewDefiningException类型,会携带对应的ModelAndView
		if (exception instanceof ModelAndViewDefiningException) {
			logger.debug("ModelAndViewDefiningException encountered", exception);
			mv = ((ModelAndViewDefiningException) exception).getModelAndView();
		}
		else {
			//由对应的处理器handler进行异常处理,返回ModelAndView。其中使用了HandlerExceptionResolver。
			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方法负责页面的渲染
		render(mv, request, 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");
		}
	}

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

	//触发Interceptor的afterCompletion方法
	if (mappedHandler != null) {
		mappedHandler.triggerAfterCompletion(request, response, null);
	}
}
           

对请求的执行结果和异常进行处理

5. DispatcherServlet#render(ModelAndView, HttpServletRequest, HttpServletResponse)

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
	//根据LocaleResolver获取Locale
	// Determine locale for request and apply it to the response.
	Locale locale =
			(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
	response.setLocale(locale);

	View view;
	String viewName = mv.getViewName();
	if (viewName != null) {
		//根据ViewResolver由viewName获取View
		// We need to resolve the view name.
		view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
		if (view == null) {
			throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
					"' in servlet with name '" + getServletName() + "'");
		}
	}
	else {
		// No need to lookup: the ModelAndView object contains the actual View object.
		view = mv.getView();
		if (view == null) {
			throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
					"View object in servlet with name '" + getServletName() + "'");
		}
	}

	// Delegate to the View object for rendering.
	if (logger.isDebugEnabled()) {
		logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
	}
	try {
		if (mv.getStatus() != null) {
			response.setStatus(mv.getStatus().value());
		}
		view.render(mv.getModelInternal(), request, response);
	}
	catch (Exception ex) {
		if (logger.isDebugEnabled()) {
			logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
					getServletName() + "'", ex);
		}
		throw ex;
	}
}
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
		Locale locale, HttpServletRequest request) throws Exception {

	if (this.viewResolvers != null) {
		for (ViewResolver viewResolver : this.viewResolvers) {
			View view = viewResolver.resolveViewName(viewName, locale);
			if (view != null) {
				return view;
			}
		}
	}
	return null;
}
           

本地化信息Locale解析,视图View解析,页面渲染,主题Theme解析

继续阅读