SpringMVC源码(四)-请求处理

原创
2017/10/16 19:05
阅读数 4.8K

上一章SpringMVC组件的初始化,所有这些初始化都是为了迎接外部的请求。当外部的请求到来时,SpringMVC如何区分不同请求类型,如何路由到正确的Controller方法,如何生成视图返回?本章就从源码分析SpringMVC的核心处理逻辑。

我们知道所有的请求都会由Servlet容器交由Servlet的doService方法,我们来看DispatcherServlet。

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

	// 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<String, Object>();
		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.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());

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

DispatcherServlet的doService方法做了三个事情

  1. 将request中所有的attribute做了备份,以在执行完include后恢复原有属性
  2. 将spring上下文,本地化解析,主题解析器绑定到request请求上
  3. 处理FlashMap

而对请求的处理交给doDispatcher方法

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

			// 决定当前请求的Handler
			mappedHandler = getHandler(processedRequest);
			if (mappedHandler == null || mappedHandler.getHandler() == null) {
				noHandlerFound(processedRequest, response);
				return;
			}

			// 决定当前请求的HandlerAdapter
			HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

			// 处理last-modified请求头
			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;
			}

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

先看doDispatcher方法执行的主要操作时序图

输入图片说明

1.请求路由

getHandler方法就是从HandlerMapping中查询匹配当前request的Handler。

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

HandlerMapping的getHandler方法在抽象基类AbstractHandlerMapping

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	// 由子类根据request获取Handler
	Object handler = getHandlerInternal(request);
	// 如果没匹配到,则获取默认Handler
	if (handler == null) {
		handler = getDefaultHandler();
	}
	if (handler == null) {
		return null;
	}
	// 如果返回的Handler为String,则使用Spring容器实例化
	if (handler instanceof String) {
		String handlerName = (String) handler;
		handler = getApplicationContext().getBean(handlerName);
	}

	// 查询匹配的拦截器,组装Handler生成HandlerExecutionChain
	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
	if (CorsUtils.isCorsRequest(request)) {
		CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
		CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
		CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
	}
	return executionChain;
}

最终返回的Handler是由拦截器链和Handler共同组成的,而具体匹配Handler的方法是交给子类来完成的。上一章组件初始化中提到生产环境下使用的是RequestMappingHandlerMapping,getHandlerInternal方法的实现在它的基类AbstractHandlerMethodMapping。

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
	// 从request获取匹配url
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
	if (logger.isDebugEnabled()) {
		logger.debug("Looking up handler method for path " + lookupPath);
	}
	this.mappingRegistry.acquireReadLock();
	try {
		// 查询匹配的HandlerMethod
		HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
		if (logger.isDebugEnabled()) {
			if (handlerMethod != null) {
				logger.debug("Returning handler method [" + handlerMethod + "]");
			}
			else {
				logger.debug("Did not find handler method for [" + lookupPath + "]");
			}
		}
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	}
	finally {
		this.mappingRegistry.releaseReadLock();
	}
}

可以看到返回的Handler的类型为HandlerMethod,它对应于Controller中的方法。上一章也提过,在AbstractHandlerMethodMapping中有一个MappingRegistry,统一管理URL和Controller方法的映射关系,lookupHandlerMethod就是对MappingRegistry的操作。

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
	List<Match> matches = new ArrayList<Match>();
	// 从mappingRegistry获取匹配到的RequestMappingInfo
	List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
	if (directPathMatches != null) {
		addMatchingMappings(directPathMatches, matches, request);
	}
	if (matches.isEmpty()) {
		// No choice but to go through all mappings...
		addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
	}

	// 对匹配项进行排序
	if (!matches.isEmpty()) {
		Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
		Collections.sort(matches, comparator);
		if (logger.isTraceEnabled()) {
			logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
					lookupPath + "] : " + matches);
		}
		Match bestMatch = matches.get(0);
		if (matches.size() > 1) {
			if (CorsUtils.isPreFlightRequest(request)) {
				return PREFLIGHT_AMBIGUOUS_MATCH;
			}
			Match secondBestMatch = matches.get(1);
			if (comparator.compare(bestMatch, secondBestMatch) == 0) {
				Method m1 = bestMatch.handlerMethod.getMethod();
				Method m2 = secondBestMatch.handlerMethod.getMethod();
				throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
						request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
			}
		}
		handleMatch(bestMatch.mapping, lookupPath, request);
		return bestMatch.handlerMethod;
	}
	else {
		// 无匹配项处理
		return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
	}
}

通过mappingRegistry匹配返回RequestMappingInfo,对应于每个有@RequestMapping注解解析后的Method。

protected RequestMappingInfo createRequestMappingInfo(
		RequestMapping requestMapping, RequestCondition<?> customCondition) {

	return RequestMappingInfo
			.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
			.methods(requestMapping.method())
			.params(requestMapping.params())
			.headers(requestMapping.headers())
			.consumes(requestMapping.consumes())
			.produces(requestMapping.produces())
			.mappingName(requestMapping.name())
			.customCondition(customCondition)
			.options(this.config)
			.build();
}

当我们拿到HandlerExecutionChain,就完成了request到Controller的路由操作。

2.适配器匹配

有了Handler后,需要合适的HandlerAdapter对其进行操作,因而就要根据Handler进行匹配。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
	for (HandlerAdapter ha : this.handlerAdapters) {
		if (logger.isTraceEnabled()) {
			logger.trace("Testing handler adapter [" + ha + "]");
		}
		if (ha.supports(handler)) {
			return ha;
		}
	}
	throw new ServletException("No adapter for handler [" + handler +
			"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

HandlerAdapter接口中定义了supports方法,用于检测是否支持Handler。生产环境使用的RequestMappingHandlerAdapter在其基类AbstractHandlerMethodAdapter中实现了supports方法。

public final boolean supports(Object handler) {
	return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

supportsInternal方法在RequestMappingHandlerAdapter的实现里默认返回true。因而RequestMappingHandlerAdapter就是用来支持类型为HandlerMethod的Handler的处理的。

3.拦截器处理

在SpringMVC中的拦截器接口HandlerInterceptor中定义了三个方法

public interface HandlerInterceptor {

	// 在Handler找到后,执行前拦截
	boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception;

	// 在Handler执行后,视图渲染前拦截
	void postHandle(
			HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
			throws Exception;

	// 请求处理完成,视图渲染后执行资源清理等
	void afterCompletion(
			HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception;

}

可以很清晰地对应到doDispatcher方法中。需要注意的有几点

  1. 前置处理preHandle,返回值为boolean。如果返回true,则执行下一个,如果返回false,则认为当前拦截器完成了请求,DispatcherServlet会直接返回,在返回前会调用所有拦截器的afterCompletion方法,完成清理工作。
  2. afterCompletion方法在遇到任何情况时都需要被执行,无论是成功返回还是抛出异常。

4.执行请求

HandlerAdapter的handle方法完成请求的真正执行。在AbstractHandlerMethodAdapter中由handleInternal执行。

protected ModelAndView handleInternal(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

	ModelAndView mav;
	checkRequest(request);


	// 执行HandlerMethod
	mav = invokeHandlerMethod(request, response, handlerMethod);

	// 处理缓存
	if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
		if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
			applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
		}
		else {
			prepareResponse(response);
		}
	}

	return mav;
}

在invokeHandlerMethod中,HandlerMethod被封装ServletInvocableHandlerMethod,包裹上方法执行需要的信息。

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

	ServletWebRequest webRequest = new ServletWebRequest(request, response);
	try {
		WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
		ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

		// 封装HandlerMethod
		ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
		invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
		invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
		invocableMethod.setDataBinderFactory(binderFactory);
		invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

		ModelAndViewContainer mavContainer = new ModelAndViewContainer();
		mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
		modelFactory.initModel(webRequest, mavContainer, invocableMethod);
		mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

		// 异步请求处理
		AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
		asyncWebRequest.setTimeout(this.asyncRequestTimeout);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.setTaskExecutor(this.taskExecutor);
		asyncManager.setAsyncWebRequest(asyncWebRequest);
		asyncManager.registerCallableInterceptors(this.callableInterceptors);
		asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

		if (asyncManager.hasConcurrentResult()) {
			Object result = asyncManager.getConcurrentResult();
			mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
			asyncManager.clearConcurrentResult();
			if (logger.isDebugEnabled()) {
				logger.debug("Found concurrent result value [" + result + "]");
			}
			invocableMethod = invocableMethod.wrapConcurrentResult(result);
		}

		// 执行处理
		invocableMethod.invokeAndHandle(webRequest, mavContainer);
		if (asyncManager.isConcurrentHandlingStarted()) {
			return null;
		}

		// 封装数据和视图
		return getModelAndView(mavContainer, modelFactory, webRequest);
	}
	finally {
		webRequest.requestCompleted();
	}
}

再到ServletInvocableHandlerMethod的invokeAndHandle方法

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {

	// 执行request
	Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
	setResponseStatus(webRequest);

	if (returnValue == null) {
		if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
			mavContainer.setRequestHandled(true);
			return;
		}
	}
	else if (StringUtils.hasText(getResponseStatusReason())) {
		mavContainer.setRequestHandled(true);
		return;
	}

	mavContainer.setRequestHandled(false);
	try {
		// 对返回值进行处理
		this.returnValueHandlers.handleReturnValue(
				returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
	}
	catch (Exception ex) {
		if (logger.isTraceEnabled()) {
			logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
		}
		throw ex;
	}
}

public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {

	// 执行方法参数
	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	if (logger.isTraceEnabled()) {
		logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
				"' with arguments " + Arrays.toString(args));
	}
	
	Object returnValue = doInvoke(args);
	if (logger.isTraceEnabled()) {
		logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
				"] returned [" + returnValue + "]");
	}
	return returnValue;
}

protected Object doInvoke(Object... args) throws Exception {
	ReflectionUtils.makeAccessible(getBridgedMethod());
	return getBridgedMethod().invoke(getBean(), args);
}

需要说明的一点是方法执行完成的返回值通过返回值处理器HandlerMethodReturnValueHandler进行处理。在RequestMappingHandlerAdapter的初始化中,内置了众多的HandlerMethodReturnValueHandler来处理多种类型的返回值。

在完成请求执行后,doDispatcher方法中做了一个默认View的设置。

applyDefaultViewName(processedRequest, mv);

private void applyDefaultViewName(HttpServletRequest request, ModelAndView mv) throws Exception {
	if (mv != null && !mv.hasView()) {
		mv.setViewName(getDefaultViewName(request));
	}
}

而这个getDefaultViewName是通过RequestToViewNameTranslator的实现类来解析的

protected String getDefaultViewName(HttpServletRequest request) throws Exception {
	return this.viewNameTranslator.getViewName(request);
}

默认实现DefaultRequestToViewNameTranslator,根据配置的一些通用url进行匹配

public String getViewName(HttpServletRequest request) {
	String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
	return (this.prefix + transformPath(lookupPath) + this.suffix);
}

5.视图渲染

当请求完成后,返回的ModelAndView需要渲染到浏览器进行显示。doDispatcher方法中processDispatchResult用来处理视图。

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
		HandlerExecutionChain mappedHandler, ModelAndView mv, 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.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;
	}

	// 完成后执行拦截器的afterCompletion
	if (mappedHandler != null) {
		mappedHandler.triggerAfterCompletion(request, response, null);
	}
}

render方法执行渲染,最终由View实现类执行

view.render(mv.getModelInternal(), request, response);

抽象类AbstractView执行对数据进行组装,输出操作交由子类完成

public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
	if (logger.isTraceEnabled()) {
		logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +
			" and static attributes " + this.staticAttributes);
	}

	// 组装数据
	Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
	prepareResponse(request, response);
	// 渲染输出
	renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}

以通用的InternalResourceView举例

protected void renderMergedOutputModel(
		Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

	// Expose the model object as request attributes.
	exposeModelAsRequestAttributes(model, request);

	// Expose helpers as request attributes, if any.
	exposeHelpers(request);

	// Determine the path for the request dispatcher.
	String dispatcherPath = prepareForRendering(request, response);

	// Obtain a RequestDispatcher for the target resource (typically a JSP).
	RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
	if (rd == null) {
		throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
				"]: Check that the corresponding file exists within your web application archive!");
	}

	// If already included or response already committed, perform include, else forward.
	if (useInclude(request, response)) {
		response.setContentType(getContentType());
		if (logger.isDebugEnabled()) {
			logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
		}
		rd.include(request, response);
	}

	else {
		// Note: The forwarded resource is supposed to determine the content type itself.
		if (logger.isDebugEnabled()) {
			logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
		}
		rd.forward(request, response);
	}
}

最终由java Servlet的RequestDispatcher完成输出。

本章中以请求的正向主流程解析了DispatcherServlet及相关类完成此过程的源码,其主要过程则是HandlerExecutionChain,HandlerMapping,HandlerAdapter,View等组件的交互过程,贴两张网上的核心原理图,希望对大家理解SpringMVC的原理有帮助。

输入图片说明

输入图片说明

展开阅读全文
打赏
3
45 收藏
分享
加载中
打赏
5 评论
45 收藏
3
分享
返回顶部
顶部