Loading

深入理解 SpringMVC

前言

SpringMVC 可以说是我们日常开发中最依赖的 Spring 组件了,它基于 Servlet 容器实现,允许我们通过注解的方式开发 Web 程序。在本篇文章,将深入 SpringMVC 源码,梳理 SpringMVC 对 Web 请求处理流程,弄懂相关核心组件的原理,最终做到在使用的时候知其然也知其所以然。

一、接受并分发Web请求

SpringMVC 的核心类是 DispatcherServlet ,它是 spring 实现的 Servlet 容器,当我们的 web 请求到达 DispatcherServlet 后,它将会根据各种规则将将请求分发到指定的处理器中,比如被 @RequestMapping 注解的方法。

和 spring 的其他核心组件一样,DispatcherServlet 也有比较复杂的层级结构,不过我们暂时不关心,仅从 Servlet 提供的 doGet 方法开始,跟踪 spring 完成一次普通请求的全流程。

1、processRequest

当我们调用 ServletdoGet / doPost 以及其他类型的方法时,请求都会被统一的转发到 processRequest 方法:


// FrameworkServlet
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {

	processRequest(request, response);
}

/**
 * Process this request, publishing an event regardless of the outcome.
 * <p>The actual event handling is performed by the abstract
 * {@link #doService} template method.
 */
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {

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

	// 初始化当前地区上下文
	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());

	// 将区域和请求参数设置当当前线程的上下文中
	initContextHolders(request, localeContext, requestAttributes);

	try {
		// 完成请求
		doService(request, response);
	}
	catch (ServletException | IOException ex) {
		failureCause = ex;
		throw ex;
	}
	catch (Throwable ex) {
		failureCause = ex;
		throw new ServletException("Request processing failed: " + ex, ex);
	}

	finally {
		// 重置请求上下文
		resetContextHolders(request, previousLocaleContext, previousAttributes);
		if (requestAttributes != null) {
			requestAttributes.requestCompleted();
		}
		logResult(request, response, failureCause, asyncManager);
		// 发布 ServletRequestHandledEvent 事件
		publishRequestHandledEvent(request, response, startTime, failureCause);
	}
}

processRequest 方法基本定义了完成请求的基本步骤:

  1. 初始化区域上下文 LocaleContext ,用于后续国际化相关的功能;
  2. 初始化请求参数对象  ServletRequestAttributes
  3. 初始化异步请求管理器 WebAsyncManager
  4. 调用 resetContextHolders 将区域上下文与请求参数对象绑定到当前线程上下文中;
  5. 调用 doService 真正的处理本次请求;
  6. 调用 resetContextHolders 重置当前请求上下文;
  7. 调用 publishRequestHandledEvent 发布请求响应事件 ServletRequestHandledEvent

2、doService

doServiceprocessRequest 的基础上进一步的为本次请求做了一些准备:

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.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 ,该集合一般用于在从定向时在两个请求之间传递参数
	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);
	}

	// 获取请求路径,并且将其放入请求参数中
	RequestPath previousRequestPath = null;
	if (this.parseRequestPath) {
		previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
		ServletRequestPathUtils.parseAndCache(request);
	}

	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);
			}
		}
		if (this.parseRequestPath) {
			ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
		}
	}
}

这些准备包括:

  • include_request 创建请求参数快照(include request 一般是指在一个请求中包含另一个请求的数据,比如通过 JSP 中的 include 标签实现的菜单栏);
  • 初始化 FlashMap ,该集合用于在重定向时在两个请求之间共享参数;
  • 解析并缓存本次请求路径;
  • 在请求参数对象中设置一些当上下文参数和配置,比如当前 Servlet 容器所在的 WebApplicationContext ,要使用的主题解析器 ThemeResolver 或地域解析器 LocaleResolver 等等;
  • 调用 doDispatch 方法,将请求分发给具体的请求处理器完成处理;
  • 移除请求参数快照,和请求路径缓存;

3、doDispatch

doDispatch 方法是真正使用对应的请求处理器完成请求的地方,它是 DispatcherServlet 最核心的方法,我们所知道的各种拦截器、处理器、视图解析以及异常处理逻辑都在这一步完成:

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 类型(表单/文件上传)的请求?
			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());

			// 处理 get 和 head 请求的缓存机制
			// 即如果是 get 或 head 请求, 就检查客户端发送的请求头中的If-Modified-Since值,如果指定的值可以与最后修改时间 lastModified  匹配,即资源没有被修改,则返回304 Not Modified响应。
			// Process last-modified header, if supported by the handler.
			String method = request.getMethod();
			boolean isGet = HttpMethod.GET.matches(method);
			if (isGet || HttpMethod.HEAD.matches(method)) {
				long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
				if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
					return;
				}
			}

			// 执行拦截器的 preHandle 方法,若有拦截器返回了 false 则立即完成本次请求
			if (!mappedHandler.applyPreHandle(processedRequest, response)) {
				return;
			}

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

			// 如果本次请求是异步请求,且以及开始,那么立刻完成本次请求
			if (asyncManager.isConcurrentHandlingStarted()) {
				return;
			}
		
			// 执行拦截器的 postHandle 放阿飞
			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 ServletException("Handler dispatch failed: " + err, err);
		}
		processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
	}

	// 执行拦截器的 afterCompletion 方法
	catch (Exception ex) {
		triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
	}
	catch (Throwable err) {
		triggerAfterCompletion(processedRequest, response, mappedHandler,
				new ServletException("Handler processing failed: " + err, 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);
			}
		}
	}
}

如果暂且不考虑异步处理相关的逻辑,那么这段代码主要完成了以下任务:

  1. 对请求进行预处理,包括检查是否为 multipart 类型的请求(表单/文件上传);
  2. 获取处理器执行链 HandlerExecutionChain 并包装为处理器适配器 HandlerAdapter
  3. 处理 GET 和 HEAD 请求的缓存机制,根据上一个 HandlerAdapter 的最后修改时间检查是否需要返回304 Not Modified响应;
  4. 执行拦截器 HandlerInterceptorpreHandle 方法,如果有任何拦截器返回 false,则立即完成请求;
  5. 调用处理器适配器的 handle 方法执行业务逻辑,并返回 ModelAndView 对象。
  6. 执行拦截器 HandlerInterceptorpostHandle 方法;
  7. 处理请求结果,包括渲染视图、处理异常等。
  8. 在处理过程中,如果发生异常,执行拦截器的 afterCompletion 方法;
  9. 清理处理multipart请求的资源。

至此,我们已经大概了解的 springMVC 处理请求的核心过程,不过要了解更详细的内容,就需要深入具体的组件中了。

二、获取请求处理器

处理器执行链 HandlerExecutionChain 是实际用于将请求打到业务逻辑的组件,在 DispatcherServlet 中需要先获取处理器映射 HandlerMapping 然后再调用它的 getHandler 方法获取:

@Nullable
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;
}

DispatcherServlet 容器刷新时,默认会通过 initHandlerMappings  从 spring 容器中获得全部的 HandlerMapping 实例。
我们常用的 @Controller@RequestMapping 的注解式配置,是通过 RequestMappingHandlerMapping 实现的。接下来我们以它为例,了解请求处理器是的运行机制。
RequestMappingHandlerMapping.png
RequestMappingHandlerMapping 有着比较复杂的层级结构,我们重点关注 AbstractHandlerMappingAbstractHandlerMethodMapping 以及 RequestMappingInfoHandlerMapping 四个层次的抽象。

1、解析处理器方法

在开始之前,我们得先知道 @Controller 中带有 @RequestMapping 的方法到底是怎么变成可以用于处理器请求的 “处理器” 的。
首先,RequestMappingInfoHandlerMappingAbstractHandlerMethodMapping 的子类,它规定了 AbstractHandlerMethodMapping 的泛型对象为 RequestMappingInfo ,实际上 RequestMappingInfo 就对应了方法上的 @RequestMapping 注解。
而谈到它是如何被从 Controller 中解析,并加载到 MappingRegistry 中的,我们就需要回到 AbstractHandlerMethodMapping 中,该抽象类实现了 InitializingBean 回调接口,在属性初始化完毕后,它将会触发 initHandlerMethods 方法:

/**
 * Detects handler methods at initialization.
 * @see #initHandlerMethods
 */
@Override
public void afterPropertiesSet() {
	initHandlerMethods();
}

/**
 * Scan beans in the ApplicationContext, detect and register handler methods.
 * @see #getCandidateBeanNames()
 * @see #processCandidateBean
 * @see #handlerMethodsInitialized
 */
protected void initHandlerMethods() {
	// 1.获取可用于解析的 bean 的 beanName
	for (String beanName : getCandidateBeanNames()) {
		// SCOPED_TARGET_NAME_PREFIX = "scopedTarget."
		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
			// 如果 beanName 不以 scopedTarget 开头,则
			processCandidateBean(beanName);
		}
	}
	// 完成解析后的回调
	handlerMethodsInitialized(getHandlerMethods());
}

/**
 * Determine the names of candidate beans in the application context.
 * @since 5.1
 * @see #setDetectHandlerMethodsInAncestorContexts
 * @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors
 */
protected String[] getCandidateBeanNames() {
	// 简而言之,扫描容器及父容器中的所有的 bean
	return (this.detectHandlerMethodsInAncestorContexts ?
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
			obtainApplicationContext().getBeanNamesForType(Object.class));
}

protected void processCandidateBean(String beanName) {
	Class<?> beanType = null;
	try {
		beanType = obtainApplicationContext().getType(beanName);
	}
	catch (Throwable ex) {
		// An unresolvable bean type, probably from a lazy bean - let's ignore it.
		if (logger.isTraceEnabled()) {
			logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
		}
	}
	if (beanType != null && isHandler(beanType)) {
		// 解析并加载处理器方法
		detectHandlerMethods(beanName);
	}
}

// 用于后续扩展的钩子方法,不过此处只是输出了一下日志
protected void handlerMethodsInitialized(Map<T, HandlerMethod> handlerMethods) {
	// Total includes detected mappings + explicit registrations via registerMapping
	int total = handlerMethods.size();
	if ((logger.isTraceEnabled() && total == 0) || (logger.isDebugEnabled() && total > 0) ) {
		logger.debug(total + " mappings in " + formatMappingName());
	}
}

// RequestMappingHandlerMapping 实现方法,即判断类上是否有 @Controller 注解
protected boolean isHandler(Class<?> beanType) {
	return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class);
}

initHandlerMethods 方法,共完成了下述步骤:

  • 通过 getCandidateBeanNames 获取 spring 容器(当前及父容器)中所有的 beanName;
  • 如果 beanName 对应的 bean :
    1. 如果不以 “scopedTarget ” 开头,即不是一个作用域代理对象(换而言之就是全局有效的);
    2. 并且 isHandler 方法确认该 bean 是一个处理器对象(即类上带有 @Controller 注解);
    3. 那么就通过 detectHandlerMethods 解析该 bean 中带有 @RequestMapping 注解的方法,变为 RequestMappingInfo 与对应的 HandlerMethod ,并注册到 MappingRegistery 中;
  • 完成上述操作后,调用 handlerMethodsInitialized 方法输出 debug 日志;

2、注册处理器方法

接下来我们继续进入 detectHandlerMethods 方法,在这个方法中,真正的完成了扫描 Controller 并解析注解方法的逻辑:

/**
 * Look for handler methods in the specified handler bean.
 * @param handler either a bean name or an actual handler instance
 * @see #getMappingForMethod
 */
protected void detectHandlerMethods(Object handler) {
	// 如果 handler 是字符串,则以其作为 beanName 从容器中获取 beanType,否则直接获取 beanType
	Class<?> handlerType = (handler instanceof String beanName ?
			obtainApplicationContext().getType(beanName) : handler.getClass());

	if (handlerType != null) {
		// 为了避免错误的拿到 CGLib 代理类,此次需要获取 bean 的真正类型
		Class<?> userType = ClassUtils.getUserClass(handlerType);
		Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
				(MethodIntrospector.MetadataLookup<T>) method -> {
					try {
						// 从方法上获取 RequestMappingInfo 对象
						return getMappingForMethod(method, userType);
					}
					catch (Throwable ex) {
						throw new IllegalStateException("Invalid mapping on handler class [" +
								userType.getName() + "]: " + method, ex);
					}
				});
		if (logger.isTraceEnabled()) {
			logger.trace(formatMappings(userType, methods));
		}
		else if (mappingsLogger.isDebugEnabled()) {
			mappingsLogger.debug(formatMappings(userType, methods));
		}

		methods.forEach((method, mapping) -> {
			// 将处理器方法和对应的元数据注册到 MappingRegister 中
			Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
			registerHandlerMethod(handler, invocableMethod, mapping);
		});
	}
}

// ====================== 获取 RequestMappingInfo ======================

// RequestMappingHandlerMapping 实现方法,即判断类上是否有 @RequestMapping 注解
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
	RequestMappingInfo info = createRequestMappingInfo(method);
	if (info != null) {
		RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
		if (typeInfo != null) {
			info = typeInfo.combine(info);
		}
		String prefix = getPathPrefix(handlerType);
		if (prefix != null) {
			info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
		}
	}
	return info;
}

private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
	RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
	RequestCondition<?> condition = (element instanceof Class ?
			getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
	// 创建 RequestMappingInfo 对象
	return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

protected RequestMappingInfo createRequestMappingInfo(
		RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
	// 这里会解析包括 method、params 等等注解属性
	RequestMappingInfo.Builder builder = RequestMappingInfo
			.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
			.methods(requestMapping.method())
			.params(requestMapping.params())
			.headers(requestMapping.headers())
			.consumes(requestMapping.consumes())
			.produces(requestMapping.produces())
			.mappingName(requestMapping.name());
	if (customCondition != null) {
		builder.customCondition(customCondition);
	}
	return builder.options(this.config).build();
}

// ====================== 注册 RequestMappingInfo 和 HandlerMethod ======================

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
	this.mappingRegistry.register(mapping, handler, method);
}

// AbstractHandlerMethodMaping.MappingRegistry
public void register(T mapping, Object handler, Method method) {
	this.readWriteLock.writeLock().lock();
	try {
		// 创建一个处理器方法
		HandlerMethod handlerMethod = createHandlerMethod(handler, method);
		// 校验一下,避免重复注册
		validateMethodMapping(handlerMethod, mapping);
		
		// 从 RequestMappingInfo 中获取请求路径(@RequestMapping 可以绑定多个路径),并添加到路径索引表
		Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
		for (String path : directPaths) {
			this.pathLookup.add(path, mapping);
		}
		
		// 通过命名策略声明请求名称,并添加到名称索引表
		String name = null;
		if (getNamingStrategy() != null) {
			name = getNamingStrategy().getName(handlerMethod, mapping);
			addMappingName(name, handlerMethod);
		}

		// 获取跨域配置,并添加到跨域索引射表
		CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
		if (corsConfig != null) {
			corsConfig.validateAllowCredentials();
			this.corsLookup.put(handlerMethod, corsConfig);
		}
		
		// 为 RequestMappingInfo 注册一个 MappingRegistration,里面有包括路径、名称、跨域配置在内的所有相关信息
		this.registry.put(mapping,
				new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
	}
	finally {
		this.readWriteLock.writeLock().unlock();
	}
}

protected HandlerMethod createHandlerMethod(Object handler, Method method) {
	// 创建一个处理器方法,注意此处仅放入了 beanName (因为 bean 可能还没有初始化完成)
	if (handler instanceof String beanName) {
		return new HandlerMethod(beanName,
				obtainApplicationContext().getAutowireCapableBeanFactory(),
				obtainApplicationContext(),
				method);
	}
	return new HandlerMethod(handler, method);
}

detectHandlerMethods 这一步做了非常多的事情,但是根据内容大概可以分为两部分:

  • 扫描被 @RequestMapping 注解的方法:
    1. 为了避免错误的拿到 CGLib 代理类,获取 bean 的真正类型;
    2. 扫描被 @RequestMapping 注解的方法,并将通过 getMappingForMethod 方法为注解创建 RequestMappingInfo
  • 通过 registerHandlerMethod 方法将 RequestMappingInfo 与对应的注解方法注册到 MappingRegistery
    1. 通过 createHandlerMethod 方法将注解方法适配为处理器方法 HandlerMethod ,并且考虑到此时 bean 可能尚未初始化,因此只记录了 beanName,而没有直接获取该 bean;
    2. 检查是否重复注册了 HandlerMethod
    3. 根据 RequestMappingInfo 中的属性建立快速索引表:
      1. 根据 path 建立请求路径索引;
      2. 根据 name 建立请求名称索引;
      3. 根据 CorsConfiguration  建立跨域索引;
    4. 创建一个包含请求元数据 RequestMappingInfo 、处理器方法 HandlerMethod 、请求路径、名称、跨域配置所有信息在内的注册信息对象 MappingRegistration ,并建立 RequestMappingInfo 与其的映射关系;

至此,我们扫描了所有的 **Controller** ,并扫描了其中带有 **@RequestMapping** 注解的方法,并最终将其变为 **RequestMappingInfo** 与对应 **HandlerMethod** ,并保存到了 **AbstractHandlerMethodMapping** 中的 **MappingRegistry** 里。

3、创建处理器执行链

先从 AbstractHandlerMapping 开始,它实现了 HandlerMappinggetHandler 方法:

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	// 获取用于处理请求的内部处理器,此处的 handler 可能是多种类型的对象
	Object handler = getHandlerInternal(request);

	// 如果没有对应的处理器,则尝试获取一个默认的处理器
	if (handler == null) {
		handler = getDefaultHandler();
	}
	if (handler == null) {
		return null;
	}

	// 如果 handler 是字符串,则将其作为 beanName 从 spring 容器查找对应的处理器
	// Bean name or resolved handler?
	if (handler instanceof String handlerName) {
		handler = obtainApplicationContext().getBean(handlerName);
	}

	// Ensure presence of cached lookupPath for interceptors and others
	if (!ServletRequestPathUtils.hasCachedPath(request)) {
		initLookupPath(request);
	}

	// 将处理器适配为处理器链
	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

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

	// 处理跨域问题
	if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
		CorsConfiguration config = getCorsConfiguration(handler, request);
		if (getCorsConfigurationSource() != null) {
			CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
			config = (globalConfig != null ? globalConfig.combine(config) : config);
		}
		if (config != null) {
			config.validateAllowCredentials();
		}
		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
	}

	return executionChain;
}

// 将内部处理器适配为处理器执行链
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
	HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain handlerExecutionChain ?
			handlerExecutionChain : new HandlerExecutionChain(handler));

	for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
		if (interceptor instanceof MappedInterceptor mappedInterceptor) {
			if (mappedInterceptor.matches(request)) {
				chain.addInterceptor(mappedInterceptor.getInterceptor());
			}
		}
		else {
			chain.addInterceptor(interceptor);
		}
	}
	return chain;
}

getHandler 中主要做了一下几件事:

  • 获取请求处理器:
    1. 先尝试通过 getHandlerInternal 获取内部请求处理器;
    2. 如果没有则尝试通过 getDefaultHandler 获取默认的请求处理器;
    3. 再没有则直接返回 null ,说明此 HandlerMapping 无法处理当前的请求;
    4. 如果请求处理器不为 null ,则它可能是多种类型的对象,其中,若其为字符串,则将其作为 beanName 从 spring 容器查找对应的处理器;
  • 将请求处理器通过 getHandlerExecutionChain 方法适配为处理器执行链 HandlerExecutionChain
    1. 若请求处理器不是一个 HandlerExecutionChain ,则新建一个 HandlerExecutionChain 包装它;
    2. 将当前 HandlerMapping 中的可以应用与当前请求的 HandlerInterceptor 全部添加到处理器执行链中,若是 MappedInterceptor 则将其内部的包装的 Interceptor 添加到执行链;
  • 若当前有跨域配置,则进行跨域处理;

4、查找可以的处理器方法

接着我们进入 AbstractHandlerMethodMapping 中的 getHandlerInternal,它进一步揭露的方法是如何适配为请求处理器的:

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
	// 获取要匹配的请求路径,一般即我们所在 @RequestMapping 中配置的 path
	String lookupPath = initLookupPath(request);
	// 加读锁
	this.mappingRegistry.acquireReadLock();
	try {
		// 根据请求路径找到与其匹配的方法
		HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	}
	finally {
		this.mappingRegistry.releaseReadLock();
	}
}

protected String initLookupPath(HttpServletRequest request) {
	if (usesPathPatterns()) {
		request.removeAttribute(UrlPathHelper.PATH_ATTRIBUTE);
		RequestPath requestPath = ServletRequestPathUtils.getParsedRequestPath(request);
		String lookupPath = requestPath.pathWithinApplication().value();
		return UrlPathHelper.defaultInstance.removeSemicolonContent(lookupPath);
	}
	else {
		return getUrlPathHelper().resolveAndCacheLookupPath(request);
	}
}

我们重点关注 lookupHandlerMethod 方法:

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
	List<Match> matches = new ArrayList<>();
	// 从 MappingRegistry 直接根据请求路径查找处理器方法,将其添加到候选列表
	List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
	if (directPathMatches != null) {
		addMatchingMappings(directPathMatches, matches, request);
	}
	if (matches.isEmpty()) {
		// 如果直接根据路径找不到匹配的方法,直接将 MappingRegistry 中的所有处理器方法添加到候选列表
		addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
	}
	if (!matches.isEmpty()) {
		// 默认首个为最匹配的,若有不止一个匹配的处理器方法,则根据 MatchComparator 从中找到最匹配的那个处理器方法
		Match bestMatch = matches.get(0);
		if (matches.size() > 1) {
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			matches.sort(comparator);
			bestMatch = matches.get(0);
			if (logger.isTraceEnabled()) {
				logger.trace(matches.size() + " matching mappings: " + matches);
			}
			if (CorsUtils.isPreFlightRequest(request)) {
				for (Match match : matches) {
					if (match.hasCorsConfig()) {
						return PREFLIGHT_AMBIGUOUS_MATCH;
					}
				}
			}
			else {
				Match secondBestMatch = matches.get(1);
				if (comparator.compare(bestMatch, secondBestMatch) == 0) {
					Method m1 = bestMatch.getHandlerMethod().getMethod();
					Method m2 = secondBestMatch.getHandlerMethod().getMethod();
					String uri = request.getRequestURI();
					throw new IllegalStateException(
							"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
				}
			}
		}
		// 该处理器方法添加到请求参数中
		request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
		handleMatch(bestMatch.mapping, lookupPath, request);
		// 获取处理器方法 HandlerMethod
		return bestMatch.getHandlerMethod();
	}
	else {
		return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
	}
}

private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
	for (T mapping : mappings) {
		// getMatchingMapping 是个抽象方法,用于根据条件获取一个与其匹配的泛型对象
		T match = getMatchingMapping(mapping, request);
		if (match != null) {
			matches.add(new Match(match, this.mappingRegistry.getRegistrations().get(mapping)));
		}
	}
}

AbstractHandlerMethodMapping 中,  getHandlerInternal 这一步主要做了以下两件事:

  • 解析并获取请求路径;
  • 根据请求路径查找对应的处理器方法 HandlerMethod ,这个过程中可能找到多个与请求参数匹配的处理器方法,但是我们借助 MatchComparator 只取最匹配的那个;

另外,值得注意的是,在这里出现两个新的东西:

  • 泛型对象 T,它是与处理器放方法 HandlerMethod 对应的元数据,用于根据 T 找到对应的处理器方法。
    比如 @RequestMapping 对应的元数据就是 T,而被注解的方法对应的就是 HandlerMethod,我们会在下文的 RequestMappingInfoHandlerMapping 更进一步了解这两者;
  • 处理器注册表 MappingRegistry ,它是 AbstractHandlerMethodMapping 中的一个内部类,用于维护泛型对象 T 与 HandlerMethod 之间的映射关系。
    在下文的 RequestMappingInfoHandlerMapping 中我们会看到 HandlerMethod 是在什么时候注册进去的;

至此,我们知道基于方法的 **HandlerMapping****getHandlerInternal**** 方法将会返回一个 **HandlerMethod** ,后续的 **HandlerExecutionChain** 将基于它创建**。

三、通过适配器执行请求

在上文,我们知道了 spring 是如何根据 HandlerMapping 获得处理器执行链 HandlerExecutionChain 的,不过实际上在 doDispatch 方法的执行过程中,它内部包装的 Handler 将被适配为一个处理器适配器 HandlerAdapter 去执行各种操作。
HandlerMapping 一样,处理器适配器同样是在 DispatcherServlet 容器刷新时,通过 initHandlerAdapters 从 spring 容器中加载的。
RequestMappingHandlerAdapter.png
它具备一个 supports 方法,用于根据通过 HandlerMapping 获得的 HandlerExecutionChain 里头包装的 Handler 对象判断是否可以应用该适配器。由于在上文我们以 RequestMappingHandlerMapping 为例讲了 HandlerMapping ,因此下文我们同样以适配 HandlerMethod 的处理器适配器 RequestMappingHandlerAdapter 为例,介绍处理器适配是如何基于处理器完成实际请求的。

1、处理器适配器

当我们调用 RequestMappingHandlerAdapterhandle 方法时:

/**
 * This implementation expects the handler to be an {@link HandlerMethod}.
 */
// AbstractHandlerMethodAdapter.handle
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
		throws Exception {

	return handleInternal(request, response, (HandlerMethod) handler);
}

// RequestMappingHandlerAdapter.handleInternal
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

	ModelAndView mav;
	checkRequest(request);

	// Execute invokeHandlerMethod in synchronized block if required.
	// 1、如果有 session,则同一时间内仅有一个线程可以处理器改 session 对应的请求
	if (this.synchronizeOnSession) {
		HttpSession session = request.getSession(false);
		if (session != null) {
			Object mutex = WebUtils.getSessionMutex(session);
			synchronized (mutex) {
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			// No HttpSession available -> no mutex necessary
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}
	}
	else {
		// 没有 session 或者不要求对 session 的请求必须同步,那就直接执行
		// No synchronization on session demanded at all...
		mav = invokeHandlerMethod(request, response, handlerMethod);
	}

	// 如果请求头中有 Cache-Control,则缓存当前请求的响应
	if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
		if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
			applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
		}
		else {
			prepareResponse(response);
		}
	}

	return mav;
}

这个方法逻辑比较简单,其实就是执行 HandlerMethod ,然后获得 ModelAndView 对象。

2、执行请求

接下来我们进入 invokeHandlerMethod 方法:

/**
 * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
 * if view resolution is required.
 * @since 4.2
 * @see #createInvocableHandlerMethod(HandlerMethod)
 */
@Nullable
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);

		// 使用 ServletInvocableHandlerMethod 包装 HandlerMethod,并且:
		// 1.设置参数解析器;
		// 2.设置返回值处理器;
		// 3.设置数据绑定工厂;
		// 4.设置方法参数名发现器;
		ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
		if (this.argumentResolvers != null) {
			invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
		}
		if (this.returnValueHandlers != null) {
			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 asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
		asyncWebRequest.setTimeout(this.asyncRequestTimeout);

		// 获取异步请求管理器,并设置并设置执行任务的线程池、异步 Web 请求和异步请求拦截器。
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.setTaskExecutor(this.taskExecutor);
		asyncManager.setAsyncWebRequest(asyncWebRequest);
		asyncManager.registerCallableInterceptors(this.callableInterceptors);
		asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
		
		// 如果存在并发结果,则获取结果并将其从管理器中清空,
		// 然后将结果也封装到 ServletInvocableHandlerMethod 中
		if (asyncManager.hasConcurrentResult()) {
			Object result = asyncManager.getConcurrentResult();
			mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
			asyncManager.clearConcurrentResult();
			LogFormatUtils.traceDebug(logger, traceOn -> {
				String formatted = LogFormatUtils.formatValue(result, !traceOn);
				return "Resume with async result [" + formatted + "]";
			});
			invocableMethod = invocableMethod.wrapConcurrentResult(result);
		}
		
		// 执行 HandlerMethod,处理请求
		invocableMethod.invokeAndHandle(webRequest, mavContainer);
		// 如果异步请求已启动(当前是个异步请求),则直接返回 null
		if (asyncManager.isConcurrentHandlingStarted()) {
			return null;
		}
		
		// 如果请求正常结束了,获取对应的视图对象
		return getModelAndView(mavContainer, modelFactory, webRequest);
	}
	finally {
		// 清空 ServletWebRequest 中的一些缓存
		webRequest.requestCompleted();
	}
}

invokeHandlerMethod 方法主要关注三个方面的内容:

  • 设置了请求方法的参数解析器,数据绑定工厂等等,这一步为我们熟悉的 @RequestParams@PathVariable 或者 @RquestBody 注解提供了支持;
  • 设置了请求的返回值处理器,这一步为 @ResponseBody 注解,与传统 MVC 中的的各种 View 提供了支持;
  • 处理了异步请求相关的逻辑,这为 SpringMVC 支持一步 Servlet 提供了支持;

异步的内容我们暂且先忽略,那么此处主要需要了解的只有参数解析器和返回值解析器了。那么,让我们进入 ServletInvocableHandlerMethodinvokeAndHandle 方法:

/**
 * Invoke the method and handle the return value through one of the
 * configured {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers}.
 * @param webRequest the current request
 * @param mavContainer the ModelAndViewContainer for this request
 * @param providedArgs "given" arguments matched by type (not resolved)
 */
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {

	// 调用方法,获得返回值,在这个过程中参数解析器和返回值解析器将会生效
	Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
	setResponseStatus(webRequest);

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

	mavContainer.setRequestHandled(false);
	Assert.state(this.returnValueHandlers != null, "No return value handlers");
	try {
		// 使用返回值解析器处理器返回值
		this.returnValueHandlers.handleReturnValue(
				returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
	}
	catch (Exception ex) {
		if (logger.isTraceEnabled()) {
			logger.trace(formatErrorForReturnValue(returnValue), ex);
		}
		throw ex;
	}
}

3、方法参数解析器

当调用 invokeForRequest 方法时,将会真正的调用方法,不过在调用前,会通过方法参数解析器 HandlerMethodArgumentResolver 从请求中解析出方法调用所需要的参数:

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

	// 解析获得方法的调用参数
	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	if (logger.isTraceEnabled()) {
		logger.trace("Arguments: " + Arrays.toString(args));
	}
	// 调用方法,执行业务逻辑
	return doInvoke(args);
}

/**
 * Get the method argument values for the current request, checking the provided
 * argument values and falling back to the configured argument resolvers.
 * <p>The resulting array will be passed into {@link #doInvoke}.
 * @since 5.1.2
 */
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {
	
	// 获取方法参数
	MethodParameter[] parameters = getMethodParameters();
	if (ObjectUtils.isEmpty(parameters)) {
		return EMPTY_ARGS;
	}

	Object[] args = new Object[parameters.length];
	for (int i = 0; i < parameters.length; i++) {
		MethodParameter parameter = parameters[i];
		parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
		args[i] = findProvidedArgument(parameter, providedArgs);
		if (args[i] != null) {
			continue;
		}
		// 确认当前的参数解析器是否支持处理器改类型的参数
		if (!this.resolvers.supportsParameter(parameter)) {
			throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
		}
		try {
			// 使用参数解析器获取方法的实际调用参数
			args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
		}
		catch (Exception ex) {
			// Leave stack trace for later, exception may actually be resolved and handled...
			if (logger.isDebugEnabled()) {
				String exMsg = ex.getMessage();
				if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
					logger.debug(formatArgumentError(parameter, exMsg));
				}
			}
			throw ex;
		}
	}
	return args;
}

@Nullable
protected static Object findProvidedArgument(MethodParameter parameter, @Nullable Object... providedArgs) {
	if (!ObjectUtils.isEmpty(providedArgs)) {
		for (Object providedArg : providedArgs) {
			if (parameter.getParameterType().isInstance(providedArg)) {
				return providedArg;
			}
		}
	}
	return null;
}

此处的逻辑没有太多复杂的逻辑,就是简单的将方法参数依次取出,然后使用参数解析器 HandlerMethodArgumentResolver 去解析得到实际用于调用的参数。比如:

  • RequestParamMethodArgumentResolver 可以根据 @RequestParam 注解的名称属性或注解参数的名称,从请求参数中获取并赋值给对应的方法参数;
  • PathVariableMethodArgumentResolver 可以从请求路径中提取占位符对应的参数值,然后将其赋给与占位符同名的带有 @PathVariable 注解的方法参数;
  • RequestResponseBodyMethodProcessor 可以从请求的 body 中读取 JSON 对象,并反序列化后赋值给带有 @RequestBody 注解的方法参数(注意,它同时也是 @ResponseBody 注解的处理器);

4、方法返回值处理器

RequestMappingHandlerAdapterinvokeAndHandle 方法中的这段代码中,spring 将会使用返回值处理器 HandlerMethodReturnValueHandler 对其进行解析:

mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
	// 使用返回值解析器处理器返回值
	// 完成请求后,返回的视图即为 ModelAndViewContainer 中的视图
	this.returnValueHandlers.handleReturnValue(
			returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
	if (logger.isTraceEnabled()) {
		logger.trace(formatErrorForReturnValue(returnValue), ex);
	}
	throw ex;
}

与方法参数解析器 HandlerMethodArgumentResolver 不同,方法返回值处理器 HandlerMethodReturnValueHandler 的主要目的是处理请求后的 View 展示问题,这也是它为何叫处理器而不是解析器的原因吧。
举个例子,在前后端不分离的时代,Controller 中方法的返回值通常会是一个相对路径,它用于表示 classpath 下的 template 文件夹中用于渲染页面的某个模板文件,后端在请求完成后会使用模板引擎将模板文件渲染为 HTML 页面,然后将其返回给发起请求的客户端(通常就是浏览器)。而在前后端分离的时代,我们通常会在方法上添加一个 @ResponseBody 注解,使得后端在完成请求后,返回一个 JSON 字符串。
实际上两者的区别在于选择了不同的返回值处理器。我们以处理 @ResponseBody 注解的 RequestResponseBodyMethodProcessor 为例:

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
		throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

	// 注意,该处理器实际上并没有向容器中设置任何 view
	mavContainer.setRequestHandled(true);
	ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
	ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

	if (returnValue instanceof ProblemDetail detail) {
		outputMessage.setStatusCode(HttpStatusCode.valueOf(detail.getStatus()));
		if (detail.getInstance() == null) {
			URI path = URI.create(inputMessage.getServletRequest().getRequestURI());
			detail.setInstance(path);
		}
	}

	// 使用 MessageConverter 序列化返回值并写入 respone 中
	// Try even with null return value. ResponseBodyAdvice could get involved.
	writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

RequestResponseBodyMethodProcessorhandleReturnValue 方法中:

  • **没有向 **ModelAndViewContainer** 中设置任何 ****View**
  • 直接通过 HttpMessageConverter 将返回值序列化后写入了 response 中;

而传统的 ModelAndViewMethodReturnValueHandler 则有着与其截然不同的实现:

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

	if (returnValue == null) {
		mavContainer.setRequestHandled(true);
		return;
	}

	ModelAndView mav = (ModelAndView) returnValue;
	if (mav.isReference()) {
		String viewName = mav.getViewName();
		mavContainer.setViewName(viewName);
		if (viewName != null && isRedirectViewName(viewName)) {
			mavContainer.setRedirectModelScenario(true);
		}
	}
	else {
		// 获取要渲染的页面,并设置到容器
		View view = mav.getView();
		mavContainer.setView(view);
		if (view instanceof SmartView && ((SmartView) view).isRedirectView()) {
			mavContainer.setRedirectModelScenario(true);
		}
	}
	mavContainer.setStatus(mav.getStatus());·
	// 将 Model 中的参数设置到容器中
	mavContainer.addAllAttributes(mav.getModel());
}

我们可以非常直观的注意到,ModelAndViewMethodReturnValueHandler **直接创建了一个 ****View** ,并将其设置到 ModelAndViewContainer 中。

值得一提的是,RequestResponseBodyMethodProcessor 同时用于支持 @RequestBody@ResponseBody ,由于读写 body 这一行为的特殊性,spring 非常贴心的提供了两个默认支持的 AOP 扩展接口 RequestBodyAdviceResponseBodyAdvice ,它们支持在读取/写入 body 之前进行一些自定义的操作。

四、异常处理

1、执行流程

现在让我们回到 DispatcherServlet 的  doDispatch 方法,在这个方法中,当我们完成 HandlerExecutionChainHandlerAdapter 的调用后,就需要针对执行结果进行处理了:

ModelAndView mv = null;
Exception dispatchException = null;

try {
	// 调用 HandlerExecutionChain 与 HandlerAdapter 
}
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 ServletException("Handler dispatch failed: " + err, err);
}

// 对执行结果或抛出的异常进行处理
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

我们进入 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;
	}

	// 触发 HandlerInterceptor 的 afterCompletion 方法
	if (mappedHandler != null) {
		// Exception (if any) is already handled..
		mappedHandler.triggerAfterCompletion(request, response, null);
	}
}

processDispatchResult 方法中,主要干了三件事:

  • 如果有运行时异常,则对捕获的异常通过 processHandlerException 进行处理,并将正常请求返回的 ModelAndView 替换为错误页面;
  • 如果 ModelAndView 不为空,那么就通过 render 方法渲染视图;
  • 如果当前请求是一个已经开始的异步请求,那么就不触发 HandlerInterceptorafterCompletion 回调方法;

考虑到现在的应用大多都是前后端分离的,因此在这里暂且忽略关于视图渲染相关的逻辑,而仅关注异常处理的  processHandlerException 方法:

protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
		@Nullable Object handler, Exception ex) throws Exception {

	// Success and error responses may use different content types
	request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);

	// Check registered HandlerExceptionResolvers...
	ModelAndView exMv = null;
	if (this.handlerExceptionResolvers != null) {
		for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
			exMv = resolver.resolveException(request, response, handler, ex);
			if (exMv != null) {
				break;
			}
		}
	}
	if (exMv != null) {
		if (exMv.isEmpty()) {
			request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
			return null;
		}
		// We might still need view name translation for a plain error model...
		if (!exMv.hasView()) {
			String defaultViewName = getDefaultViewName(request);
			if (defaultViewName != null) {
				exMv.setViewName(defaultViewName);
			}
		}
		if (logger.isTraceEnabled()) {
			logger.trace("Using resolved error view: " + exMv, ex);
		}
		else if (logger.isDebugEnabled()) {
			logger.debug("Using resolved error view: " + exMv);
		}
		WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
		return exMv;
	}

	throw ex;
}

这里的 HandlerExceptionResolver 就是我们一般说的异常拦截器,当然,我们更熟悉的可能是基于 @ControllerAdvice@ExceptionHandler 实现的全局异常拦截器,不过实际上它也是基于 HandlerExceptionResolver 实现的。

2、全局异常拦截器

严格来说,spring 中并没有名为全局异常拦截器的 HandlerExceptionResolver ,我们一般用于实现该效果的组件是 ExceptionHandlerExceptionResolver ,借助它,我们可以在一个类上添加 @ControllerAdvice 将其声明为控制器切面,然后再在其中的方法上通过 @ExceptionHandler 注解声明该方法用于处理指定类型的异常(实际上不加注解,而是直接让方法参数的类型是异常也可以)。
ExceptionHandlerExceptionResolver 实现了 InitializingBean 回调接口:

@Override
public void afterPropertiesSet() {
	// Do this first, it may add ResponseBodyAdvice beans
	// 加载所有被 @ControllerAdvice 注解的 bean 中的所有带有 @ExceptionHandler 注解的方法
	initExceptionHandlerAdviceCache();

	// 加载默认的参数解析器
	if (this.argumentResolvers == null) {
		List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
		this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
	}
	// 加载默认的返回值解析器
	if (this.returnValueHandlers == null) {
		List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
		this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
	}
}

private void initExceptionHandlerAdviceCache() {
	if (getApplicationContext() == null) {
		return;
	}

	// 通过 ControllerAdviceBean 获取容器中所有带有 @ControllerAdvice 注解的 bean
	List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
	for (ControllerAdviceBean adviceBean : adviceBeans) {
		Class<?> beanType = adviceBean.getBeanType();
		if (beanType == null) {
			throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
		}
		// 创建 ExceptionHandlerMethodResolver,它会扫描类中所有带有 @ExceptionHandler 注解的方法
		ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
		if (resolver.hasExceptionMappings()) {
			this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
		}
		if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
			this.responseBodyAdvice.add(adviceBean);
		}
	}

	if (logger.isDebugEnabled()) {
		int handlerSize = this.exceptionHandlerAdviceCache.size();
		int adviceSize = this.responseBodyAdvice.size();
		if (handlerSize == 0 && adviceSize == 0) {
			logger.debug("ControllerAdvice beans: none");
		}
		else {
			logger.debug("ControllerAdvice beans: " +
					handlerSize + " @ExceptionHandler, " + adviceSize + " ResponseBodyAdvice");
		}
	}
}

/**
 * Return the list of argument resolvers to use including built-in resolvers
 * and custom resolvers provided via {@link #setCustomArgumentResolvers}.
 */
protected List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
	List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();

	// Annotation-based argument resolution
	resolvers.add(new SessionAttributeMethodArgumentResolver());
	resolvers.add(new RequestAttributeMethodArgumentResolver());

	// Type-based argument resolution
	resolvers.add(new ServletRequestMethodArgumentResolver());
	resolvers.add(new ServletResponseMethodArgumentResolver());
	resolvers.add(new RedirectAttributesMethodArgumentResolver());
	resolvers.add(new ModelMethodProcessor());

	// Custom arguments
	if (getCustomArgumentResolvers() != null) {
		resolvers.addAll(getCustomArgumentResolvers());
	}

	// Catch-all
	resolvers.add(new PrincipalMethodArgumentResolver());

	return resolvers;
}

/**
 * Return the list of return value handlers to use including built-in and
 * custom handlers provided via {@link #setReturnValueHandlers}.
 */
protected List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
	List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();

	// Single-purpose return value types
	handlers.add(new ModelAndViewMethodReturnValueHandler());
	handlers.add(new ModelMethodProcessor());
	handlers.add(new ViewMethodReturnValueHandler());
	handlers.add(new HttpEntityMethodProcessor(
			getMessageConverters(), this.contentNegotiationManager, this.responseBodyAdvice));

	// Annotation-based return value types
	handlers.add(new ServletModelAttributeMethodProcessor(false));
	handlers.add(new RequestResponseBodyMethodProcessor(
			getMessageConverters(), this.contentNegotiationManager, this.responseBodyAdvice));

	// Multi-purpose return value types
	handlers.add(new ViewNameMethodReturnValueHandler());
	handlers.add(new MapMethodProcessor());

	// Custom return value types
	if (getCustomReturnValueHandlers() != null) {
		handlers.addAll(getCustomReturnValueHandlers());
	}

	// Catch-all
	handlers.add(new ServletModelAttributeMethodProcessor(true));

	return handlers;
}

在这个方法中,spring 做了这些事情:

  • 加载容器中所有带有 @ControllerAdvice 注解的 bean,然后为其创建异常处理器方法解析器 ExceptionHandlerMethodResolver ,若里面能解析到至少一个异常处理器方法,那么就将它缓存到 exceptionHandlerAdviceCache 中;
  • 加载必要的参数解析器 HandlerMethodArgumentResolver 与返回值处理器 HandlerMethodReturnValueHandler ,其中很大一部分与 RequestMappingHandlerMapping 用到的一样,这也是为什么我们可以在被 @ExceptionHandler 注解的方法上面加 @ResponseBody 注解还能生效的原因;

3、异常处理器方法解析器

当我们在 initExceptionHandlerAdviceCache 的时候,会为每一个被 @ControllerAdvice 注解的 bean 创建一个异常拦截器方法解析器 ExceptionHandlerMethodResolver

public class ExceptionHandlerMethodResolver {
	/**
	 * A filter for selecting {@code @ExceptionHandler} methods.
	 */
	public static final MethodFilter EXCEPTION_HANDLER_METHODS = method ->
			AnnotatedElementUtils.hasAnnotation(method, ExceptionHandler.class);
	
	// 拦截的异常与处理异常的方法的对应关系
	private final Map<Class<? extends Throwable>, Method> mappedMethods = new HashMap<>(16);
	
	// 具体的异常类与对应的处理方法的缓存,用来避免每次处理异常的时候都要遍历 mappedMethods 寻找匹配的异常类
	private final Map<Class<? extends Throwable>, Method> exceptionLookupCache = new ConcurrentReferenceHashMap<>(16);
	
	/**
	 * A constructor that finds {@link ExceptionHandler} methods in the given type.
	 * @param handlerType the type to introspect
	 */
	public ExceptionHandlerMethodResolver(Class<?> handlerType) {
		for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
			for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
				addExceptionMapping(exceptionType, method);
			}
		}
	}

	/**
	 * Extract exception mappings from the {@code @ExceptionHandler} annotation first,
	 * and then as a fallback from the method signature itself.
	 */
	@SuppressWarnings("unchecked")
	private List<Class<? extends Throwable>> detectExceptionMappings(Method method) {
		List<Class<? extends Throwable>> result = new ArrayList<>();
		detectAnnotationExceptionMappings(method, result);
		// 如果方法上没有 @ExceptionHandler 注解,那么就根据参数中是否有异常类型的参数确认方法支持处理的异常类型
		if (result.isEmpty()) {
			for (Class<?> paramType : method.getParameterTypes()) {
				if (Throwable.class.isAssignableFrom(paramType)) {
					result.add((Class<? extends Throwable>) paramType);
				}
			}
		}
		if (result.isEmpty()) {
			throw new IllegalStateException("No exception types mapped to " + method);
		}
		return result;
	}
	
	private void detectAnnotationExceptionMappings(Method method, List<Class<? extends Throwable>> result) {
		// 根据方法上的 @ExceptionHandler 注解确认方法支持处理哪几类异常
		ExceptionHandler ann = AnnotatedElementUtils.findMergedAnnotation(method, ExceptionHandler.class);
		Assert.state(ann != null, "No ExceptionHandler annotation");
		result.addAll(Arrays.asList(ann.value()));
	}
	
	private void addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) {
		// 每种类型的异常只能有一个方法与其对应
		Method oldMethod = this.mappedMethods.put(exceptionType, method);
		if (oldMethod != null && !oldMethod.equals(method)) {
			throw new IllegalStateException("Ambiguous @ExceptionHandler method mapped for [" +
					exceptionType + "]: {" + oldMethod + ", " + method + "}");
		}
	}
}

ExceptionHandlerMethodResolver 创建的时候,会遍历类中的每一个方法:

  • 若方法有 @ExceptionHandler 注解,那么就根据注解确认方法支持处理哪几种类型的异常,否则就根据方法参数是否有 Throwable 类型的参数推测支持处理哪几种异常;
  • mappedMethods 中记录异常类型与方法的对应关系,并且每种类型的异常只能有一个方法与其对应;

4、异常处理器方法的调用

当我们在 DispatcherServletprocessHandlerException 方法调用异常解析器时,将会遍历所有的 ExceptionHandlerMethodResolver ,若遍历到 ExceptionHandlerExceptionResolver 则会调用其 resolveException 方法:

// AbstractHandlerExceptionResolver
public ModelAndView resolveException(
		HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {

	// 是否支持处理该异常
	if (shouldApplyTo(request, handler)) {
		prepareResponse(ex, response);
		ModelAndView result = doResolveException(request, response, handler, ex);
		if (result != null) {
			// Print debug message when warn logger is not enabled.
			if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
				logger.debug(buildLogMessage(ex, request) + (result.isEmpty() ? "" : " to " + result));
			}
			// Explicitly configured warn logger in logException method.
			logException(ex, request);
		}
		return result;
	}
	else {
		return null;
	}
}

// =================== 是否支持处理该异常 & 处理器 ===================

// AbstractHandlerExceptionResolver
protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
	if (handler != null) {
		if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
			return true;
		}
		if (this.mappedHandlerClasses != null) {
			for (Class<?> handlerClass : this.mappedHandlerClasses) {
				if (handlerClass.isInstance(handler)) {
					return true;
				}
			}
		}
	}
	return !hasHandlerMappings();
}

// AbstractHandlerMethodExceptionResolver,重写自 AbstractHandlerExceptionResolver
@Override
protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
	if (handler == null) {
		return super.shouldApplyTo(request, null);
	}
	// 如果 handler 是 HandlerMethod,使用 @RequestMapping 声明的处理器就是 HandlerMethod 的一种
	else if (handler instanceof HandlerMethod handlerMethod) {
		handler = handlerMethod.getBean();
		return super.shouldApplyTo(request, handler);
	}
	else if (hasGlobalExceptionHandlers() && hasHandlerMappings()) {
		return super.shouldApplyTo(request, handler);
	}
	else {
		return false;
	}
}

// =================== 处理异常 ===================

// AbstractHandlerExceptionResolver
@Override
@Nullable
protected final ModelAndView doResolveException(
		HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {

	HandlerMethod handlerMethod = (handler instanceof HandlerMethod ? (HandlerMethod) handler : null);
	return doResolveHandlerMethodException(request, response, handlerMethod, ex);
}

// ExceptionHandlerExceptionResolver
@Override
@Nullable
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
		HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {

	// 创建一个请求对象,注意,此处的处理流程与 RequestMappingHandlerAdapter 的 invokeHandlerMethod 高度一致
	// 因此从某种程度上来说,我们可以认为它相当于重走了一遍 @RequestMapping 方法
	ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
	if (exceptionHandlerMethod == null) {
		return null;
	}

	// 设置参数解析器器与返回值处理器
	if (this.argumentResolvers != null) {
		exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
	}
	if (this.returnValueHandlers != null) {
		exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
	}

	ServletWebRequest webRequest = new ServletWebRequest(request, response);
	ModelAndViewContainer mavContainer = new ModelAndViewContainer();

	ArrayList<Throwable> exceptions = new ArrayList<>();
	try {
		if (logger.isDebugEnabled()) {
			logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod);
		}
		// Expose causes as provided arguments as well
		Throwable exToExpose = exception;
		while (exToExpose != null) {
			exceptions.add(exToExpose);
			Throwable cause = exToExpose.getCause();
			exToExpose = (cause != exToExpose ? cause : null);
		}
		Object[] arguments = new Object[exceptions.size() + 1];
		exceptions.toArray(arguments);  // efficient arraycopy call in ArrayList
		arguments[arguments.length - 1] = handlerMethod;
		exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, arguments);
	}
	catch (Throwable invocationEx) {
		// Any other than the original exception (or a cause) is unintended here,
		// probably an accident (e.g. failed assertion or the like).
		if (!exceptions.contains(invocationEx) && logger.isWarnEnabled()) {
			logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, invocationEx);
		}
		// Continue with default processing of the original exception...
		return null;
	}

	if (mavContainer.isRequestHandled()) {
		return new ModelAndView();
	}
	else {
		ModelMap model = mavContainer.getModel();
		HttpStatusCode status = mavContainer.getStatus();
		ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
		mav.setViewName(mavContainer.getViewName());
		if (!mavContainer.isViewReference()) {
			mav.setView((View) mavContainer.getView());
		}
		if (model instanceof RedirectAttributes) {
			Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
			RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
		}
		return mav;
	}
}

@Nullable
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
		@Nullable HandlerMethod handlerMethod, Exception exception) {

	Class<?> handlerType = null;

	if (handlerMethod != null) {
		// Local exception handler methods on the controller class itself.
		// To be invoked through the proxy, even in case of an interface-based proxy.
		// 检查 Controller 里面是不是已经有异常处理器方法,如果有就先用本地的
		handlerType = handlerMethod.getBeanType();
		ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.computeIfAbsent(
				handlerType, ExceptionHandlerMethodResolver::new);
		Method method = resolver.resolveMethod(exception);
		if (method != null) {
			return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method, this.applicationContext);
		}
		// For advice applicability check below (involving base packages, assignable types
		// and annotation presence), use target class instead of interface-based proxy.
		if (Proxy.isProxyClass(handlerType)) {
			handlerType = AopUtils.getTargetClass(handlerMethod.getBean());
		}
	}
	
	// 如果该方法所在的 Controller 中没有异常处理器方法,那么再用 @ControllerAdvice 中的异常处理器方法
	for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
		ControllerAdviceBean advice = entry.getKey();
		if (advice.isApplicableToBeanType(handlerType)) {
			ExceptionHandlerMethodResolver resolver = entry.getValue();
			Method method = resolver.resolveMethod(exception);
			if (method != null) {
				return new ServletInvocableHandlerMethod(advice.resolveBean(), method, this.applicationContext);
			}
		}
	}

	return null;
}

// =================== 寻找支持处理异常的方法 ===================

// ExceptionHandlerMethodResolver
@Nullable
public Method resolveMethod(Exception exception) {
	return resolveMethodByThrowable(exception);
}

// ExceptionHandlerMethodResolver
@Nullable
public Method resolveMethodByThrowable(Throwable exception) {
	// 根据异常类型寻找支持处理的方法
	Method method = resolveMethodByExceptionType(exception.getClass());
	if (method == null) {
		// 如果找不到,就递归获取异常堆栈的下一层异常进行匹配
		Throwable cause = exception.getCause();
		if (cause != null) {
			method = resolveMethodByThrowable(cause);
		}
	}
	return method;
}

// ExceptionHandlerMethodResolver
@Nullable
public Method resolveMethodByExceptionType(Class<? extends Throwable> exceptionType) {
	// 先从缓存里面找该类型对应的处理器方法,没有再遍历处理器方法进行匹配
	Method method = this.exceptionLookupCache.get(exceptionType);
	if (method == null) {
		method = getMappedMethod(exceptionType);
		this.exceptionLookupCache.put(exceptionType, method);
	}
	return (method != NO_MATCHING_EXCEPTION_HANDLER_METHOD ? method : null);
}

private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
	// 遍历处理器方法进行匹配
	List<Class<? extends Throwable>> matches = new ArrayList<>();
	for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
		// 异常类型可以是要处理的异常类型的父类或者父接口
		if (mappedException.isAssignableFrom(exceptionType)) {
			matches.add(mappedException);
		}
	}

	// 如果找到了多个支持的异常处理器方法,那么根据他们支持的异常类在继承树中的深度进行匹配
	// 比如,一个处理器支持处理 Throwable,而另一个支持处理 RuntimeException,那么将优先选择后者
	if (!matches.isEmpty()) {
		if (matches.size() > 1) {
			matches.sort(new ExceptionDepthComparator(exceptionType));
		}
		return this.mappedMethods.get(matches.get(0));
	}
	else {
		return NO_MATCHING_EXCEPTION_HANDLER_METHOD;
	}
}

这一段代码非常长,但是整体逻辑并不复杂,总共就按顺序干了三件事:

  • 根据 shouldApplyTo 判断是否支持处理该异常;
  • 通过 getExceptionHandlerMethod 方法获取用于处理该类型异常的处理器方法:
    1. 先为请求方法所在的 Controller 创建 ExceptionHandlerMethodResolver ,看看里面有没有支持处理该异常的方法,如果有就优先使用本地的异常处理方法;
    2. 遍历缓存的已有ExceptionHandlerMethodResolver ,一次调用它们的 resolveMethod 方法;
      1. 调用 resolveMethodByThrowable 方法,在里面尝试通过 resolveMethodByExceptionType 方法找到支持处理该异常的方法,若没有则递归异常堆栈中的所有异常直到找到为止;
      2. resolveMethodByExceptionType 中先根据异常类型寻找缓存的处理器方法,如果没有就调用 getMappedMethod 方法再次寻找并缓存对应的异常处理器方法;
      3. getMappedMethod 中遍历所有的支持处理该异常的处理器方法:
        1. 若只找到一个,那么就直接返回该方法;
        2. 若找到多个,则比较它们支持处理的异常类型的继承树中的深度,并返回深度最大的那个异常。
          举个例子,若现在又两个处理器方法,一个支持 Throwable,而另一个支持 RuntimeException,那么将优先选择后者;
  • doResolveHandlerMethodException 方法中完成对错误请求的处理,这个步骤大部分与 RequestMappingHandlerAdapterinvokeHandlerMethod 方法一致:
    1. 完成步骤二,为异常找到对应的异常处理器方法,获得与该请求对应的 ServletInvocableHandlerMethod 对象;
    2. 设置必要的方法参数解析器与方法返回值处理器;
    3. 调用 ServletInvocableHandlerMethod ,完成对异常的处理,并获得 ModelAndViewContainer

最终,异常解析器返回的 ModelAndViewContainer 将会替代正常的 HandlerAdapter 返回的 mv 容器。

总结

1、接受并分发 Web 请求

  • DispatcherServlet 是 Spring MVC 的核心类,它实现了 Servlet 接口。其父类为 FrameworkServlet,它在内部实现了 Servlet 中定义的 doXXX 方法(比如 doGetdoPost);
  • FrameworkServlet 接收到请求时,会将对 Servlet 的请求被转发到内部的 processRequest 方法,在这个方法中,它做了下述三件事:
    1. 将会初始化一些诸如国际化、请求参数、异步管理器之类的上下文资源;
    2. 调用 doService 方法完成请求;
    3. 在请求后释放资源,并且发布 ServletRequestHandledEvent 事件;
  • 其中,doService 方法又进一步对请求做了一些处理,包括缓存请求参数、生成重定向时使用的参数闪存等,并最终调用 doDispatch 方法真正的执行请求;
  • DispatcherServlet 实现了 doDispatch 抽象方法,在这一步将真正的完成请求:
    1. 找到可应用于该请求的 HandlerMapping 获取处理器执行器 HandlerExecutionChain ,它包含必要的拦截器 HandlerInterceptor 与执行业务请求的处理器 Handler
    2. 根据获取到的处理器 Handler,找到并包装为对应的处理器适配器 HandlerAdapter ,里面包含了参数解析、返回值解析等逻辑;
    3. 执行所有拦截器 HandlerInterceptor  的 preHandle 方法;
    4. 执行处理器适配器 HandlerAdapterhandle 方法,并真正的触发处理器 Handler 中的逻辑;
    5. 执行所有拦截器 HandlerInterceptor  的 postHandle 方法;
    6. 对执行结果进行解析,如果存在异常则使用执行器异常解析器 HandlerExceptionResolver 对异常进行处理;
    7. 执行所有拦截器 HandlerInterceptorafterCompletion 方法;

2、获取请求的处理器

  • HandlerMapping 是 MVC 中用于获取处理器执行链 HandlerExecutionChain 的核心组件,我们平时基于 @Controller + @RequestMapping 的注解式开发依赖其实现类 RequestMappingHandlerMapping
  • RequestMappingHandlerMapping 完成属性初始化时,将会触发 afterPropertiesSet 回调,此时它将会扫描容器中所有带有 @Controller 注解的 bean,并解析其中带有 @RequestMapping 注解的方法,然后将注解的信息解析为 RequestMappingInfo 作为 key,方法封装为 HandlerMethod (它就是后面要用的处理器 Handler 的一种)作为 value,并注册到内部的映射注册表 MappingRegistery 中;
  • 当通过 RequestMappingHandlerMappinggetHandler 方法获取处理器链时,将会遍历 MappingRegistry 中的 key —— 也就是 RequestMappingInfo ,并根据请求类型、 URL 等参数获取一个最匹配的 HandlerMethod ,并将 RequestMappingHandlerMapping 中的拦截器 HandlerInterceptor **与处理器 HandlerMethod 一起封装为 RequestMappingHandlerMapping

3、通过适配器执行请求

  • 当获取到处理器执行链 HandlerExecutionChain 后,就需要根据 HandlerExecutionChain 中的 Handler —— 也就是根据被 @RequestMapping 注解的方法得到的 HandlerMethod —— 找到对应的处理器适配 HandlerAdapterDispatchServlet 中会注册有多个 HandlerAdapter ,但是最终只会根据 Handler 找到唯一一个可用的 HandlerAdapter ,一般情况下就是 RequestMappingHandlerAdapter
  • 当调用 RequestMappingHandlerAdapterinvokeHandlerMethod 方法后,HandlerMethod 会被包装为 ServletInvocableHandlerMethod ,并分别为其设置好以下几个关键组件:
    1. 参数解析器 HandlerMethodArgumentResolver
    2. 返回值处理器 HandlerMethodReturnValueHandler
    3. 视图容器 ModelAndViewContainer
    4. 异步管理器 WebAsyncManager
  • 接着调用 ServletInvocableHandlerMethodinvokeAndHandle 方法触发整个执行流程:
    1. 调用参数解析器 HandlerMethodArgumentResolver 获取本次方法调用的真实参数,我们熟悉的一些注解就是在这一步完成处理的:
      • @RequestParam 对应的处理器为 RequestParamMethodArgumentResolver ,它会从 Request 请求中将请求参数与方法参数一一对应;
      • @PathVariable 对应的处理器为 PathVariableMethodArgumentResolver ,它将会从请求路径中提取参数并对应的方法参数中;
      • @RequestBody 对应的处理器为 RequestResponseBodyMethodProcessor ,它将会从请求头的 body 中读取 json 参数并反序列为方法参数对象,并且在这个过程中会执行 RequestBodyAdvice 回调;
    2. 调用 HandlerMethod ,触发用户定义的业务逻辑,并获得返回值;
    3. 使用返回值处理器 HandlerMethodReturnValueHandler 处理 HandlerMethod 调用后的返回值,根据返回值处理器的不同将会有不同的处理逻辑:
      • 如果我们的方法直接或间接被 @ResponseBody 注解,那么 RequestResponseBodyMethodProcessor 将会把方法的返回值序列化为 json 字符串并写入响应体中,并且在这个过程中会执行ResponseBodyAdvice 回调。最终不会往视图容器 ModelAndViewContainer  中放入任何 View
      • 如果我们的方法返回 ModelAndView 对象, ModelAndViewMethodReturnValueHandler 则会把返回值中的 View 扔到视图容器 ModelAndViewContainer 中;

4、异常处理

  • 当执行 HandlerAdapterhandle 方法的过程中抛出了异常,则异常将交由 DispatchServletprocessHandlerException 进行处理,该方法中将获取可以用于处理该异常的异常解析器 HandlerExceptionResolver 去处理异常。
    异常拦截器有很多种,我们比较熟悉的 @ControllerAdvice + @ExceptionHandler 的方式实际上就是通过 ExceptionHandlerExceptionResolver 完成的;
  • ExceptionHandlerExceptionResolver 完成属性初始化后,会触发 afterPropertiesSet 回调,在这个方法中,它将会:
    1. 检查所有被 @ControllerAdvice 注解的 bean 中的是否存在带有 @ExceptionHandler 注解的方法,若有则将该 bean 添加至缓存;
    2. 加载默认的参数解析器 HandlerMethodArgumentResolver 与返回值处理器 HandlerMethodReturnValueHandler —— 这里与与 RequestMappingHandlerMapping 几乎一样,这也是为什么被 @ExceptionHandler 注解的方法同样支持 @ResponseBody 等注解的原因;
  • 当通过 ExceptionHandlerExceptionResolverresolveException 方法处理异常时,它将会遍历缓存中被 @ControllerAdvice 注解的 bean,并检查其中的所有方法:
    1. 若方法有 @ExceptionHandler 注解,那么就根据注解确认方法支持处理哪几种类型的异常;
    2. 若方法没有注解,但是有 Throwable 类型的参数,就根据参数类型推测支持处理哪几种异常;
    3. 若方法没有注解,且没有 Throwable 类型的参数,就跳过它;

所有符合条件的方法都将被包装为 ExceptionHandlerMethodResolver 并缓存在 ExceptionHandlerExceptionResolver 中;

  • 当处理器异常时,会根据异常的类型去从缓存中寻找对应的 ExceptionHandlerMethodResolver
    1. 若只找到一个,那么就直接返回该 ExceptionHandlerMethodResolver
    2. 若找到多个,则比较它们支持处理的异常类型的继承树中的深度,并返回深度最大的那个异常。
      举个例子,若现在又两个处理器方法,一个支持 Throwable,而另一个支持 RuntimeException,那么将优先选择后者;
  • 若找到可用的 RequestMappingHandlerAdapter,那么将通过与 RequestMappingHandlerAdapter 类似的流程走完对异常处理方法的调用,并最终使用返回的 ModelAndView (如果有)替换 HandlerAdapter 得到的 ModelAndView
posted @ 2024-03-20 23:42  Createsequence  阅读(21)  评论(0编辑  收藏  举报