Spring与MVC(四)

在上一篇文章Spring与MVC(三)中,我们简单地分析了DispatcherServlet的处理请求过程。在这篇文章中,我们详细分析一下DispatcherServlet在处理请求时是如何找到正确的Controller,以及执行。

几个重要的接口和类

HandlerMethod

HandlerMethod是一个封装了方法参数、方法注解、方法返回值等众多元素的类。

它的子类InvocableHandlerMethod有两个重要的属性WebDataBinderFactoryHandlerMethodArgumentResolverComposite,很明显是对请求进行处理的。

InvocableHandlerMethod的子类ServletInvocableHandlerMethod有个重要的属性HandlerMethodReturnValueHandlerComposite,很明显是对响应进行处理的。

ServletInvocableHandlerMethod这个类在HandlerAdapter对每个请求处理过程中,都会实例化一个出来(上面提到的属性由HandlerAdapter进行设置),分别对请求和返回进行处理。

MethodParameter

HandlerMethod类中的parameters属性类型,是一个MethodParameter数组。MethodParameter是一个封装了方法参数具体信息的工具类,包括参数的索引位置,类型,注解,参数名等信息。

HandlerMethod在实例化的时候,构造函数中会初始化这个数组,这时只初始化部分数据,在HandlerAdapter对请求处理过程中会完善其他属性,之后交与合适的HandlerMethodArgumentResolver接口处理。

RequestCondition

RequestCondition是SpringMVC的映射基础中的请求条件,可以进行combine,compareTo,getMatchingCondition操作。这个接口是映射匹配的关键接口,其中getMatchingCondition方法关乎是否能找到合适的映射。

RequestMappingInfo

RequestMappingInfo是一个封装了各种请求映射条件并实现了RequestCondition接口的类。

包含各种RequestCondition实现类属性,patternsConditionmethodsConditionparamsConditionheadersConditionconsumesConditionproducesCondition,分别代表http请求的路径模式、方法、参数、头部等信息。

RequestMappingHandlerMapping

RequestMappingHandlerMapping

与寻找Controller关系最密切的一个类是RequestMappingHandlerMapping,我们首先来看一下这个类在初始化时所做的工作:

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
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));

for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
try {
beanType = getApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
}
}
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
}
handlerMethodsInitialized(getHandlerMethods());
}

首先找出Spring容器中所有的bean。

然后遍历这些bean,调用isHandler方法判断这个bean是不是handler,isHandler判断该类的注解中是否有@Controller@RequestMapping注解。如果这个bean是handler则调用detectHandlerMethods方法获取handlerMethod。

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
protected void detectHandlerMethods(final Object handler) {
Class<?> handlerType = (handler instanceof String ?
getApplicationContext().getType((String) handler) : handler.getClass());
final Class<?> userType = ClassUtils.getUserClass(handlerType);

Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
new MethodIntrospector.MetadataLookup<T>() {
@Override
public T inspect(Method method) {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
}
});

if (logger.isDebugEnabled()) {
logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
}
for (Map.Entry<Method, T> entry : methods.entrySet()) {
Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
T mapping = entry.getValue();
registerHandlerMethod(handler, invocableMethod, mapping);
}
}

调用MethodIntrospector.selectMethods方法获取handler中所有的方法与RequestMappingInfo的映射,返回一个Map<Method, RequestMappingInfo>映射。其中的关键方法是getMappingForMethod,具体实现由子类RequestMappingHandlerMapping实现:

1
2
3
4
5
6
7
8
9
10
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);
}
}
return info;
}

getMappingForMethod方法通过方法以及类上加的@RequestMapping的注释来创建RequestMappingInfo。创建RequestMappingInfo的工作由createRequestMappingInfo方法完成:

1
2
3
4
5
6
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

回到getMappingForMethod方法,从method中获得RequestMappingInfo之后再从类中获得RequestMappingInfo,然后调用combine方法将两者结合起来返回:

1
2
3
4
5
6
7
8
9
10
11
12
13
public RequestMappingInfo combine(RequestMappingInfo other) {
String name = combineNames(other);
PatternsRequestCondition patterns = this.patternsCondition.combine(other.patternsCondition);
RequestMethodsRequestCondition methods = this.methodsCondition.combine(other.methodsCondition);
ParamsRequestCondition params = this.paramsCondition.combine(other.paramsCondition);
HeadersRequestCondition headers = this.headersCondition.combine(other.headersCondition);
ConsumesRequestCondition consumes = this.consumesCondition.combine(other.consumesCondition);
ProducesRequestCondition produces = this.producesCondition.combine(other.producesCondition);
RequestConditionHolder custom = this.customConditionHolder.combine(other.customConditionHolder);

return new RequestMappingInfo(name, patterns,
methods, params, headers, consumes, produces, custom.getCondition());
}

我们可以看到,两个RequestMappingInfo的结合就是将RequestMappingInfo中各个元素结合起来再创建一个新的RequestMappingInfo返回。

回到detectHandlerMethods方法,获取到MethodRequestMappingInfo的映射之后。调用registerHandlerMethod方法注册这个映射关系,本质是在MappingRegistry类的mappingLookup中添加mapping与handlerMethod的映射关系;在urlLoopup中添加url与mapping的映射关系;在registry中添加mapping与MappingRegistration的映射关系。

HandlerExecutionChain的获取

经过上一篇文章Spring与MVC(三)的分析,我们已经知道DispatcherServlet.doDispatch是真正处理请求的方法,方法中首先调用getHandler方法获取HandlerExecutionChain,其中包括了目标方法以及拦截器。下面我们来更详细地分析一下这个获取HandlerExecutionChain的过程。

首先来看getHandler

1
2
3
4
5
6
7
8
9
10
11
12
13
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}

遍历当前handlerMappings中保存的HandlerMapping,与当前请求相关的是RequestMappingHandlerMapping,其中getHandler方法在RequestMappingHandlerMapping的父类AbstractHandlerMapping中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
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 = getApplicationContext().getBean(handlerName);
}

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

首先调用getHandlerInternal方法根据请求获取对应的处理方法(HandlerMethod),该方法在AbstractHandlerMapping的子类AbstractHandlerMethodMapping中实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
this.mappingRegistry.acquireReadLock();
try {
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();
}
}

首先调用getLookupPathForRequest方法获取这个request请求的路径。

然后调用AbstractHandlerMethodMapping.lookupHandlerMethod方法寻找HandlerMethod

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
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
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.getMappingsByUrlmappingRegistry中获取请求路径对应的RequestMappingInfo

然后调用addMatchingMappings获取请求路径对应的Match列表,这个列表中包含RequestMappingInfo以及对应的HandlerMethod

经过对其中RequestMappingInfo的排序,获得一个最佳的匹配(如果第一个匹配和第二个匹配是相同的话,抛出异常),返回其中的HandlerMethod

addMatchingMappings方法

addMatchingMappings方法的功能就是遍历所有的请求映射关系,寻找所有匹配请求路径的方法。调用流程如下:

  1. RequestMappingInfoHandlerMapping.getMatchingMapping
  2. RequestMappingInfo.getMatchingConditon

RequestMappingInfo.getMatchingConditon方法中调用PatternsRequestConditon.getMatchingConditon检查请求路径与Mapping中的映射路径规则是否匹配,调用流程如下:

  1. PatternsRequestCondition.getMatchingPatterns
  2. PatternsRequestCondition.getMatchingPattern
  3. AntPathMatcher.match

可以看到路径的匹配检查调用的是AntPathMatcher.match方法。

回到getHandler方法。通过getHandlerInternal方法获得请求对应的HandlerMethod之后,再调用getHandlerExecutionChain方法获得HandlerExecutionChain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
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以及HandlerInterceptor(拦截器)列表:包含ConversionServiceExposingInterceptor, ResourceUrlProviderExposingInterceptor

HandlerExecutionChain的执行

回到doDispatch方法,调用getHandlerAdapter方法根据HandlerExecutionChain中的handler获取HandlerAdapter

1
2
3
4
5
6
7
8
9
10
11
12
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");
}

这里的handlerAdapters有三个(RequestMappingHandlerAdapterHttpRequestHandlerAdapterSimpleControllerHandlerAdapter),遍历然后判断是否支持handler(主要是通过handler的类型来判断),这里返回的是RequestMappingHandlerAdapter

回到doDispatch方法。在正式调用handler之前首先调用applyPreHandle执行拦截器的preHandle方法。

然后调用HandlerAdapter.handle方法执行handler方法。

AbstractHandlerMethodAdapter.handle方法委托给RequestMappingHandlerAdapter.handleInternal方法来执行。

handleInternal方法调用RequestMappingHandler.invokeHandlerMethod来执行handler。

invokeHandlerMethod方法调用ServletInvocableHandlerMethod.invokeAndHandle

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

首先调用invokeForRequest执行用户定义的逻辑方法,然后调用HandlerMethodReturnValueHandlerComposite.handleReturnValue来处理Controller方法返回的值:

1
2
3
4
5
6
7
8
9
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

首先调用selectHandler方法获取处理返回值的处理器(HandlerMethodReturnValueHandler),对于渲染页面来说这里返回的是ViewNameMethodReturnValueHandlerViewNameMethodReturnValueHandler.handleReturnValue方法中在mavContainer中设置viewName。

回到RequestMappingHandlerAdapter.invokeHandlerMethod,得到了渲染页面的名称之后,再调用getModelAndView方法来创建ModelAndView

返回到Dispatcher.doDispatch方法,执行完handle方法,获得了ModelAndView,接着调用applyPostHandle执行拦截器的postHandle方法。最后调用processDispatchResult方法渲染页面或者处理异常。