DispatcherServlet 核心流程剖析

警告
本文最后更新于 2020-12-16,文中内容可能已过时。

spring源码系列文章,示例代码的中文注释,均是 copy 自 https://gitee.com/wlizhi/spring-framework

链接中源码是作者从 github 下载,并以自身理解对核心流程及主要节点做了详细的中文注释。


1 处理请求的入口

根据 servlet 规范,当服务器在接收到请求时,会调用至 servlet.service() 方法,进行处理,DispatcherServlet 就是如此。

例如一个post请求,会经过以下调用:

service() -> doPost() -> processRequest() -> doService() -> doDispatch()

无论什么类型的请求,最终都会调用至 doDispatch()。

doDispatch() 源码如下:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
public class DispatcherServlet extends FrameworkServlet {

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;
		/* TODO
		    预处理:
		        1. 获取HandlerMethod和过滤器链的包装类(url-hanlderMethod映射):HandlerExecutionChain,里面包含了handlerMethod对象
		        2. 根据handlerMethod对象,找到合适的HandlerAdapter对象,这里用到了策略模式
		    调用链:
		        1. 前置拦截,正向遍历调用,若有返回false,则调用后置处理,返回。
				    interceptor.preHandle(request, response, this.handler)
				    triggerAfterCompletion(request, response, null);
			    2. 根据HandlerAdapter获取真正的处理类,执行:((Controller) handler).handleRequest(request, response)
			    3. 中置拦截,反向遍历调用。 interceptor.postHandle(request, response, this.handler, mv);
			    4. 视图渲染,在视图渲染之后,会在视图渲染方法中调用后置处理。
			        render(mv, request, response);
			        if (mappedHandler != null) {
						mappedHandler.triggerAfterCompletion(request, response, null);
				    }
			*/
		// 异步管理
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				// 文件上传
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// TODO 这个方法很重要,重点看 获取封装了 handlerMethod、拦截器的执行链封装 HandlerExecutionChain。
				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// TODO 获取跟HandlerMethod匹配的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);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				// TODO 前置过滤器,如果为false则直接返回
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// TODO 调用到Controller具体方法,核心方法调用,重点看看
				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

				applyDefaultViewName(processedRequest, mv);
				// TODO 执行中置拦截
				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);
			}
			// TODO 处理结果,这里执行了后置拦截。在前面的流程里捕获了业务方法执行过程中抛出的异常。
			//  如果上面的流程中抛出了异常,则dispatchException一定有值。
			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);
				}
			}
		}
	}
}

主要调用流程如下:

  1. 获取到 HandlerExecutionChain。其中封装了 handlerMethod 和 拦截器链。
  2. 获取 HandlerAdapter。
  3. 调用拦截器前置拦截方法 preHandle()。
  4. 调用 handle(),内部通过反射调用了controller中的业务方法。
  5. 调用 applyPostHandle(),执行中置拦截。
  6. 调用 processDispatchResult(),内部调用了全局异常处理和后置拦截。

前面五个步骤是在 try catch 块中执行的,如果执行中发生了异常,会被捕获到,变量 dispatchException 也就有值了,这时候才会触发全局异常处理。

2 GetHandler() 获取handlerMethod及拦截器的封装类

getHandler() 源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class DispatcherServlet extends FrameworkServlet {
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		// handlerMappering实例
		if (this.handlerMappings != null) {
			// 在 DispatcherServlet 初始化的时候,会将handlerMappings的值,赋值到这里。
			for (HandlerMapping mapping : this.handlerMappings) {
				// 获取HandlerMethod和拦截器链的包装类
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}
}

遍历 handlerMappings,依次调用 getHandler(),如果获取到执行链,就返回。

getHandler() 源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
		implements HandlerMapping, Ordered, BeanNameAware {
	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		// TODO 获取 HandlerMethod
		Object handler = getHandlerInternal(request);
		if (handler == null) {
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}
		// Bean name or resolved handler?
		if (handler instanceof String) {
			String handlerName = (String) handler;
			handler = obtainApplicationContext().getBean(handlerName);
		}
		// TODO 将 handlerMethod,包装到 HandlerExecutionChain 中。
		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

		// 省略无关代码...
		return executionChain;
	}
}

这里有两个关键的方法调用,getHandlerInternal()、getHandlerExecutionChain()。

2.1 GetHandlerInternal()

源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {

    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		this.mappingRegistry.acquireReadLock();
		try {
			// 根据路径获取到 HandlerMapping,再根据 HandlerMapping 获取到 HandlerMethod
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			// TODO 因为在初始化 HandlerMethod 时,并没有将bean实例封装进去,只是封装了一个 beanClass 和 beanName。
			//  如果获取到 handlerMethod 不为空,则会在这里进行 getBean 操作,将 bean 实例封装到 handlerMethod中。
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}
}

首先根据请求路径,获取到 HandlerMethod。如果 HandlerMethod 存在,则将内部封装的 beanName,通过调用 getBean(),获取到具体的实例, 将获取到的实例缓存到成员变量中,替换掉变量 beanName。


lookupHandlerMethod() 中,会首先通过 url 获取到 HandlerMappingInfo,再通过 HandlerMappingInfo 获取到具体的 handlerMethod。

lookupHandlerMethod中的部分源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {

    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		List<Match> matches = new ArrayList<>();
		// 根据url路径从 urlLookup 中获取 RequestMappingInfo
		List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
		if (directPathMatches != null) {
			addMatchingMappings(directPathMatches, matches, request);
		}
		// 省略部分代码...
		if (!matches.isEmpty()) {
			// 获取最佳匹配,代码省略...
			return bestMatch.handlerMethod;
		} // 省略部分代码...
    }
}

如以上源码,在 getMappingsByUrl() 中就获取了 RequestMappingInfo 实例列表。是从成员变量 urlLookup 中获取的。

addMatchingMappings() 中,从成员变来那个 mappingLookup 获取到了 handlerMethod。

源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
    
	private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
		for (T mapping : mappings) {
			T match = getMatchingMapping(mapping, request);
			if (match != null) {
				// 从 mappingLookup 中获取 handlerMethod
				matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
			}
		}
	}

	public Map<T, HandlerMethod> getMappings() {
			return this.mappingLookup;
	}
}

上面代码中,注意 getMappings() 方法返回的是 this.mappingLookup,这是一个 map 集合,封装了 RequestMappingInfo - HandlerMethod 的映射关系。


createWithResolvedBean() 源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class HandlerMethod {
	public HandlerMethod createWithResolvedBean() {
		Object handler = this.bean;
		// 最初这个 bean 成员变量,只是设置了一个 beanName,因此它是字符串类型的,执行到这里会调用 getBean(),将真正的实例设置到这里。
		// 如果已经设置过了,bean就不是字符串类型了,就不会进这个 if 语句。
		if (this.bean instanceof String) {
			Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
			String beanName = (String) this.bean;
			handler = this.beanFactory.getBean(beanName);
		}
		return new HandlerMethod(this, handler);
	}
}

前文中有提到,在 RequestMappingHandlerMapping 初始化的时候,是收集了HandlerMethod的,而在封装 HandlerMethod 时,并没有设置具体的 bean 实例,仅仅设置了 beanClass 以及 beanName。bean 的实例就是在这里进行设置的。

如上源码,当容器启动后,第一个请求进来时,this.bean 中存储的是 beanName,也就是一个字符串。这时候就会触发调用 getBean(),将 this.bean 的值替换为 bean 的实例对象。

后面再有请求过来,就不会再调用 getBean 了。这里的 new HandlerMethod(),类似于一种copy。因为在执行过程中,可能会修改内部封装的一些属性值,即每一个线程获取到的这个对象要是线程安全的(或者说是线程独立的),所以才要重新创建一个 HandlerMethod 实例。

2.2 GetHandlerExecutionChain()

getHandlerExecutionChain() 源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
		implements HandlerMapping, Ordered, BeanNameAware {
	protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
		// 创建拦截器链
		HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
				(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
		// 拦截器匹配,获取拦截器执行链
		String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
		// 遍历所有的拦截器,如果是 MappedInterceptor,根据拦截器的 excludePatterns 和 includePatterns 来进行匹配。
		// 如果没有配置 includePatterns 且在 excludePatterns 没有匹配到,则默认为 true。
		// 也就是说 MappedInterceptor 中具有路径匹配功能,同时包装了一个 HandlerInterceptor 实现。
		// 而 HandlerInterceptor 中仅仅定义了 前置、中置、后置拦截方法。
		for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
			if (interceptor instanceof MappedInterceptor) {
				MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
				if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
					chain.addInterceptor(mappedInterceptor.getInterceptor());
				}
			}
			else {
				chain.addInterceptor(interceptor);
			}
		}
		return chain;
	}
}

首先创建 HandlerExecutionChain,将 HandlerMethod 封装。 遍历所有的拦截器,如果拦截器是 MappedInterceptor 类型,则调用 matches() 进行路径匹配,匹配为 true,就加入到拦截器执行链中。这里的拦截器链就是一个 List。

HandlerExecutionChain 的数据结构:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class HandlerExecutionChain {

	private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
	/** 这里是一个 handlerMethod */
	private final Object handler;
	/** 拦截器 最终会合并到 interceptorList */
	@Nullable
	private HandlerInterceptor[] interceptors;
    /** 拦截器 */
	@Nullable
	private List<HandlerInterceptor> interceptorList;

	private int interceptorIndex = -1;

    public void addInterceptor(HandlerInterceptor interceptor) {
		initInterceptorList().add(interceptor);
	}

	private List<HandlerInterceptor> initInterceptorList() {
		if (this.interceptorList == null) {
			this.interceptorList = new ArrayList<>();
			// 如果 interceptors 中有值,合并到 interceptorList 中。
			if (this.interceptors != null) {
				// An interceptor array specified through the constructor
				CollectionUtils.mergeArrayIntoCollection(this.interceptors, this.interceptorList);
			}
		}
		this.interceptors = null;
		return this.interceptorList;
	}
}

从其数据结构中可以看出,内部就是封装了 handler 和多个拦截器组成的 interceptorList 列表。

在添加拦截器时,会调用 initInterceptorList(),如果 interceptors 中有值,会合并到列表中。

如此,就将 HandlerMethod 以及匹配的拦截器封装为 HandlerExecutionChain 返回了。

3 GetHandlerAdapter()

这里是典型的策略模式,从 handlerAdapters 中遍历,依次调用 supports() 判断是否支持当前的 handlerMethod,如果支持,就返回。如果最终未找到有支持的 HandlerAdapter,会抛出异常。

这也是为什么在初始化的时候,会默认注册那么多 HandlerAdapter 的原因。spring 考虑到了大多数的一些 参数、返回值 等的处理策略。

源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class DispatcherServlet extends FrameworkServlet {

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		// 根据handlerMethod对象,找到合适的HandlerAdapter对象,这里是典型的的策略模式
		// 遍历所有的 handlerAdapters,并调用其 supports(),如果有 adapter 支持这个 handlerMethod,就返回这个 adapter。
		if (this.handlerAdapters != null) {
			for (HandlerAdapter adapter : this.handlerAdapters) {
				if (adapter.supports(handler)) {
					return adapter;
				}
			}
		}
		throw new ServletException("No adapter for handler [" + handler +
				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
	}
}

4 前置拦截 - ApplyPreHandle()

源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public class HandlerExecutionChain {
    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = 0; i < interceptors.length; i++) {
				HandlerInterceptor interceptor = interceptors[i];
				// 遍历执行拦截器的前置方法,只要有一个返回了false,就返回false。
				// 每次执行一个前置拦截后,就将interceptorIndex+1,当有前置方法返回false时,执行triggerAfterCompletion。
				if (!interceptor.preHandle(request, response, this.handler)) {
					// 倒序执行后置拦截,起始索引是 interceptorIndex,即如果也有前置拦截执行失败了,
					// 那么相应的这个失败的及之后的拦截器的后置拦截就不会执行。
					triggerAfterCompletion(request, response, null);
					return false;
				}
				this.interceptorIndex = i;
			}
		}
		return true;
	}
}

调用执行链中的方法,遍历执行拦截器的前置拦截方法,每成功执行一个,将成员变量 interceptorIndex 的值 +1,在这之间如果前置拦截返回 false,就会跳过中置拦截,直接触发后置拦截。

也就是说,如果有任何一个前置拦截返回了 false,那么所有的中置拦截方法都不会执行。

后置拦截的执行时倒序的,并不一定是从最后一个开始,而是根据最后一个返回 true 的前置拦截的索引 interceptorIndex 开始,倒序执行到列表的开头。

后置拦截的源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public class HandlerExecutionChain {

    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
			throws Exception {

		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			// 倒序执行,起始索引是最后一个返回 true 的前置拦截的索引。
			for (int i = this.interceptorIndex; i >= 0; i--) {
				HandlerInterceptor interceptor = interceptors[i];
				try {
					interceptor.afterCompletion(request, response, this.handler, ex);
				}
				catch (Throwable ex2) {
					logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
				}
			}
		}
	}
}

5 中置拦截 - ApplyPostHandle()

源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class HandlerExecutionChain {
    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
			throws Exception {

		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			// 如果存在拦截器,倒序执行中置拦截。能执行到这个方法,说明所有的前置拦截都成功执行了。
			for (int i = interceptors.length - 1; i >= 0; i--) {
				HandlerInterceptor interceptor = interceptors[i];
				interceptor.postHandle(request, response, this.handler, mv);
			}
		}
	}
}

中置拦截是从最后一个索引开始,倒序执行的。能执行到这里,那么所有的前置拦截一定全部返回了true。所以这里可以不用根据前面记录的索引来执行,而是直接从最后一个索引开始,倒序执行。

6 全局异常处理及后置拦截 - ProcessDispatchResult()

源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class DispatcherServlet extends FrameworkServlet {

    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 {
				// TODO 异常处理
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(request, response, handler, exception);
				errorView = (mv != null);
			}
		}
		// 省略部分代码...
		if (mappedHandler != null) {
			// 执行后置拦截
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}
}

在外围方法中,是捕获了具体方法执行过程中,抛出的异常的。

如果业务方法中抛出了异常,则会作为参数传递到这里。如果存在异常,就会调用全局异常处理。

最后执行后置拦截。

异常处理也是通过策略模式实现,在接口 HandlerExceptionResolver 中,只定义了 resolveException(),而在其抽象子类中定义了 shouldApplyTo(),这个方法判断了当前的异常处理类是否支持指定的异常。

遍历所有的异常处理实例,首先判断是否支持,如果支持处理当前异常,就会调用 doResolveHandlerMethodException()。

doResolveHandlerMethodException() 大体分为两步,首先获取异常处理方法对象,然后执行异常处理方法。

源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
		implements ApplicationContextAware, InitializingBean {
    protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
			HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
		// TODO 获取异常处理方法
		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);
		}

		// 省略部分代码...
        Throwable cause = exception.getCause();
        if (cause != null) {
            // 异常处理方法的执行。
            // Expose cause as provided argument as well
            exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
        }
		// 省略部分代码...
	}
}

ServletInvocableHandlerMethod 的获取源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
		implements ApplicationContextAware, InitializingBean {

    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.
			handlerType = handlerMethod.getBeanType();
			ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
			if (resolver == null) {
				// ExceptionHandlerMethodResolver定义了对注解@ExceptionHandler的支持
				// 查找@ControllerAdvice标注的处理类中,标注有 @ExceptionHandler 的方法,并将注解中配置的 Exception类型与方法的反射对象
				// 映射关系封装缓存起来。如果一个controller中抛出了异常,就可以根据这个异常从异常处理类中找到这个处理方法了。
				resolver = new ExceptionHandlerMethodResolver(handlerType);
				this.exceptionHandlerCache.put(handlerType, resolver);
			}
			Method method = resolver.resolveMethod(exception);
			if (method != null) {
				return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
			}
			// 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());
			}
		}
        // 省略部分代码...
		return null;
	}
}

如上源码,这里用到了缓存。第一次执行时,会创建一个 ExceptionHandlerMethodResolver。

ExceptionHandlerMethodResolver 构造函数中,就会查找异常处理类中所有带有 @ExceptionHandler 的方法。

源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class ExceptionHandlerMethodResolver {

	public static final MethodFilter EXCEPTION_HANDLER_METHODS = method ->
			AnnotatedElementUtils.hasAnnotation(method, ExceptionHandler.class);

	public ExceptionHandlerMethodResolver(Class<?> handlerType) {
        // 使用反射查找处理类中的所有带有 @ExceptionHandler 注解的方法。
		for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
			for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
				addExceptionMapping(exceptionType, method);
			}
		}
	}
}

异常的处理逻辑源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {

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

		// TODO 重点:具体方法调用逻辑
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		// 省略部分代码...
        // TODO 使用返回值处理器,对返回值进行处理
        this.returnValueHandlers.handleReturnValue(
                returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		// 省略部分代码...
	}
}

首先反射调用异常处理方法,然后调用返回值处理。

反射调用逻辑:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class InvocableHandlerMethod extends HandlerMethod {

	public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
		// TODO 重点:获取参数数组
		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		// TODO 方法的反射执行
		return doInvoke(args);
	}
}

handleReturnValue() 中会处理返回值。比如有页面跳转、json数据流的响应,他们的处理方式是不一样的。通过策略模式,选择合适的处理类进行处理。

源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {

	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
		// TODO 查找返回值处理器,策略模式的一种运用
		HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
		// 如果不存在返回值处理器,则跑出异常
		if (handler == null) {
			throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
		}
		// 对返回值进行处理
		handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
	}
}

处理类的选择源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {

	private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
		boolean isAsyncValue = isAsyncReturnValue(value, returnType);
		// 遍历容器中所有的返回值处理器,判断是否支持,如果有返回值处理器支持这个返回值,则返回此处理器。
		// 这也是策略模式的一种运用。
		for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
			if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
				continue;
			}
			if (handler.supportsReturnType(returnType)) {
				return handler;
			}
		}
		return null;
	}
}              

与之前流程中用到的策略模式,一样的套路。

判断是否支持处理对应的返回值,如果支持,就调用对应的 handleReturnValue()。