之前分析过 SpringMVC
中的 DispatcherServlet
,分析了 SpringMVC
处理请求的过程。但忽略了一些 DispatcherServlet
协助请求处理的组件,例如 SpringMVC
中的 HandlerMapping
、 HandlerAdapter
、 ViewResolvers
等等。
HandlerMappings
在 DispathServlet
中主要作用是为请求的 urlpath
匹配对应的 Controller
,建立一个映射关系,根据请求查找 Handler
、 Interceptor
。 HandlerMappings
将请求传递到 HandlerExecutionChain
上, HandlerExecutionChain
包含了一个能够处理该请求的处理器,还可以包含拦截改请求的拦截器。
在没有处理器映射相关配置情况下, DispatcherServlet
会为你创建一个 BeanNameUrlHandlerMapping
作为默认映射的配置。在 DispatchServlet.properties
文件中对于 HandlerMapping
的默认配置是:
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,/ org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
HandlerMapping
的配置策略一般分为配置式 BeanNameUrlHandlerMapping
和注解式 DefaultAnnotationHandlerMapping
。不过 DefaultAnnotationHandlerMapping
已经被放弃了,取代它的是 RequestMappingHandlerMapping
,不知道为啥 SpringMVC
这个默认配置尚未做修改。
AbstractHandlerMapping
是 HandlerMapping
的抽象实现,是所有 HandlerMapping
实现类的父类。
AbstractHandlerMapping
的作用是是为了初始化 Interceptors
。 AbstractHandlerMapping
重写了 WebApplicationObjectSupport
的 initApplicationContext
方法。
protected void initApplicationContext()throwsBeansException{ extendInterceptors(this.interceptors); detectMappedInterceptors(this.adaptedInterceptors); initInterceptors(); }
extendInterceptors
方法, Springmvc
并没有做出具体实现,这里留下一个拓展,子类可以重写这个模板方法,为子类添加或者修改 Interceptors
。
detectMappedInterceptors
方法将 SpringMVC
容器中所有 MappedInterceptor
类的 bean
添加到 adaptedInterceptors
中。
initInterceptors
初始化拦截器。遍历 interceptors
将 WebRequestInterceptor
和 HandlerInterceptor
类型的拦截器添加到 adaptedInterceptors
中。 HandlerMapping
通过 getHandler
方法来获取请求的处理器 Handler
和拦截器 Interceptor
。在 getHandlerExecutionChain
方法中将遍历之前初始化的 adaptedInterceptors
,为当前的请求选择对应的 MappedInterceptors
和 adaptedInterceptors
。
AbstractUrlHandlerMapping
继承于 AbstractHandlerMapping
,它是通过 URL
来匹配具体的 Handler
。 AbstractUrlHandlerMapping
维护一个 handlerMap
来存储 Url
和 Handler
的映射关系。
AbstractUrlHandlerMapping
重写了 AbstractHandlerMapping
类中的 getHandlerInternal
方法。 HandlerMapping
通过 getHandler
方法,就会调用这里的 getHandlerInternal
方法来获取 Handler
。 getHandlerInternal
方法中关键调用 lookupHandler
方法去获取 handler
。
protectedObjectlookupHandler(String urlPath, HttpServletRequest request)throwsException{ // Direct match? Object handler = this.handlerMap.get(urlPath); if (handler != null) { // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } validateHandler(handler, request); return buildPathExposingHandler(handler, urlPath, urlPath, null); } // Pattern match? List<String> matchingPatterns = new ArrayList<String>(); for (String registeredPattern : this.handlerMap.keySet()) { if (getPathMatcher().match(registeredPattern, urlPath)) { matchingPatterns.add(registeredPattern); } else if (useTrailingSlashMatch()) { if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) { matchingPatterns.add(registeredPattern +"/"); } } } String bestMatch = null; Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath); if (!matchingPatterns.isEmpty()) { Collections.sort(matchingPatterns, patternComparator); if (logger.isDebugEnabled()) { logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns); } bestMatch = matchingPatterns.get(0); } if (bestMatch != null) { handler = this.handlerMap.get(bestMatch); if (handler == null) { if (bestMatch.endsWith("/")) { handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1)); } if (handler == null) { throw new IllegalStateException( "Could not find handler for best pattern match [" + bestMatch + "]"); } } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } validateHandler(handler, request); String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath); // There might be multiple 'best patterns', let's make sure we have the correct URI template variables // for all of them Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>(); for (String matchingPattern : matchingPatterns) { if (patternComparator.compare(bestMatch, matchingPattern) == 0) { Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath); Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars); uriTemplateVariables.putAll(decodedVars); } } if (logger.isDebugEnabled()) { logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables); } return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables); } // No handler found... return null; }
lookupHandler
方法来获取 handler
。在 lookupHandler
方法中,先通过 URL
在 handlerMap
查找是否有合适的 handler
。 handler
,遍历 handlerMap
利用正则匹配的方法,找到符合要求的 handlers
(有可能是多个)。 Ant
风格,将会通过排序筛选出一个匹配程度最高的 Handler
。 buildPathExposingHandler
方法构建一个 handler
,添加 PathExposingHandlerInterceptor
和 UriTemplateVariablesHandlerInterceptor
两个拦截器并返回。 上面介绍获取 handler
的过程中,会先从 handlerMap
查找。下面看一下 handlerMap
是如何初始化的。 AbstractUrlHandlerMapping
是通过 registerHandler
初始化 handlerMap
的。 AbstractUrlHandlerMapping
共有两个 registerHandler
方法。分别是注册多个 url
到一个 handler
和注册一个 url
到一个 handler
。首先判断 handlerMap
是否有此 handler
。如果存在的话,判断是否一致,不一致则抛出异常,如果不存在的话,如果 url
是 /
、 /*
,则,返回 root handler
、 default handler
,如果不是将添加到 handlerMap
中。
SimpleUrlHandlerMapping
继承于 AbstractUrlHandlerMapping
。 SimpleUrlHandlerMapping
重写了父类 AbstractHandlerMapping
中的初始化方法 initApplicationContext
。在 initApplicationContext
方法中调用 registerHandlers
方法。
protected void registerHandlers(Map<String, Object> urlMap)throwsBeansException{ if (urlMap.isEmpty()) { logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping"); } else { for (Map.Entry<String, Object> entry : urlMap.entrySet()) { String url = entry.getKey(); Object handler = entry.getValue(); // Prepend with slash if not already present. if (!url.startsWith("/")) { url = "/" + url; } // Remove whitespace from handler bean name. if (handler instanceof String) { handler = ((String) handler).trim(); } registerHandler(url, handler); } } }
判断是 url
是否以 /
开头,如果不是,默认补齐 /
,确保所有的url都是以 /
开头,然后依次调用父类的 registerHandler
方法注册到 AbstractUrlHandlerMapping
中的 handlerMap
。
在使用 SimpleUrlHandlerMapping
时,需要在注册的时候配置其 urlmap
否则会抛异常。
AbstractDetectingUrlHandlerMapping
类继承于 AbstractUrlHandlerMapping
类,重写了 initApplicationContext
方法,在 initApplicationContext
方法中调用了 detectHandlers
方法。
protected void detectHandlers()throwsBeansException{ if (logger.isDebugEnabled()) { logger.debug("Looking for URL mappings in application context: " + getApplicationContext()); } String[] beanNames = (this.detectHandlersInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class)); // Take any bean name that we can determine URLs for. for (String beanName : beanNames) { String[] urls = determineUrlsForHandler(beanName); if (!ObjectUtils.isEmpty(urls)) { // URL paths found: Let's consider it a handler. registerHandler(urls, beanName); } else { if (logger.isDebugEnabled()) { logger.debug("Rejected bean name '" + beanName + "': no URL paths identified"); } } } }
获取所有容器的 beanNames
,遍历所有的 beanName
,调用 determineUrlsForHandler
方法解析 url
,这里的 determineUrlsForHandler
也是运用了模板方法设计模式,具体的实现在其子类中,如果解析到子类,将注册到父类的 handlerMap
中。
BeanNameUrlHandlerMapping
类的类图大致如下:
BeanNameUrlHandlerMapping
类继承于 AbstractDetectingUrlHandlerMapping
类。重写了父类中的 determineUrlsForHandler
方法。
protected String[] determineUrlsForHandler(String beanName) { List<String> urls = new ArrayList<String>(); if (beanName.startsWith("/")) { urls.add(beanName); } String[] aliases = getApplicationContext().getAliases(beanName); for (String alias : aliases) { if (alias.startsWith("/")) { urls.add(alias); } } return StringUtils.toStringArray(urls); }
其通过 beanName
解析 Url
规则也很简单,判断 beanName
是否以 /
开头。
BeanNameUrlHandlerMapping
是 SpringMVC
的默认映射配置。
通常我们也习惯于用 @Controller
、 @Re questMapping
来定义 Handler
, AbstractHandlerMethodMapping
可以将 method
作为 Handler
来使用。
AbstractHandlerMethodMapping
实现了 InitializingBean
接口,实现了 afterPropertiesSet
方法。当容器启动的时候会调用 initHandlerMethods
注册委托 handler
中的方法。
public void afterPropertiesSet(){ initHandlerMethods(); } 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()); }
在 initHandlerMethods
方法中,做了以下工作:
BeanFactoryUtils
扫描应用上下文,获取所有的 bean
。 beanName
,调用 isHandler
方法判断是目标 bean
是否包含 @Controller
或 @RequestMapping
注解。 @Controller
或 @RequestMapping
注解的类,调用 detectHandlerMethods
委托处理,获取所有的 method
,并调用 registerHandlerMethod
注册所有的方法。 在 detectHandlerMethods
方法负责将 Handler
保存到 Map
中。
protected void detectHandlerMethods(finalObject handler){ // 获取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 publicTinspect(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); } }
selectMethods
方法中重写了 MetadataLookup
中的 inspect
方法, inspect
方法中调用了子类 RequestMappingHandlerMapping
实现了 getMappingForMethod
模板方法,用于构建 RequestMappingInfo
。
public static <T> Map<Method, T>selectMethods(Class<?> targetType,finalMetadataLookup<T> metadataLookup){ final Map<Method, T> methodMap = new LinkedHashMap<Method, T>(); Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>(); Class<?> specificHandlerType = null; if (!Proxy.isProxyClass(targetType)) { handlerTypes.add(targetType); specificHandlerType = targetType; } handlerTypes.addAll(Arrays.asList(targetType.getInterfaces())); for (Class<?> currentHandlerType : handlerTypes) { final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType); ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() { @Override public void doWith(Method method){ Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass); T result = metadataLookup.inspect(specificMethod); if (result != null) { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) { methodMap.put(specificMethod, result); } } } }, ReflectionUtils.USER_DECLARED_METHODS); } return methodMap; }
在 selectMethods
通过反射获取所有的方法,重写了 doWith
方法,将 handler
中的 method
和请求对应的 RequestMappingInfo
保存到 methodMap
中。
最终 detectHandlerMethods
将遍历这个 methodMap
,调用 registerHandlerMethod
注册 HandlerMethod
到 MappingRegistry
。
在 AbstractHandlerMethodMapping
类中,有个内部类 MappingRegistry
,用来存储 mapping
和 handler methods
注册关系,并提供了并发访问方法。
AbstractHandlerMethodMapping
通过 getHandlerInternal
来为一个请求选择对应的 handler
。
protectedHandlerMethodgetHandlerInternal(HttpServletRequest request)throwsException{ // 根据request获取对应的urlpath String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); if (logger.isDebugEnabled()) { logger.debug("Looking up handler method for path " + lookupPath); } // 获取读锁 this.mappingRegistry.acquireReadLock(); try { // 调用lookupHandlerMethod方法获取请求对应的HandlerMethod HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); if (logger.isDebugEnabled()) { if (handlerMethod != null) { logger.debug("Returning handler method [" + handlerMethod + "]"); } else { logger.debug("Did not find handler method for [" + lookupPath + "]"); } } return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } }
lookupHandlerMethod
的具体实现如下:
protectedHandlerMethodlookupHandlerMethod(String lookupPath, HttpServletRequest request)throwsException{ List<Match> matches = new ArrayList<Match>(); // 通过lookupPath获取所有匹配到的path List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) { // 将匹配条件添加到matches addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { // 如果没有匹配条件,将所有的匹配条件都加入matches 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 + "}"); } } // 将lookupPath设为请求request的PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE属性 handleMatch(bestMatch.mapping, lookupPath, request); return bestMatch.handlerMethod; } else { return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); } }
整个过程以 Match
作为载体, Match
是个内部类,封装了匹配条件和 handlerMethod
两个属性,默认的实现是将 lookupPath
设置为请求的属性。
###总结
本文从源码角度上分析了 HandlerMapping
的各种实现。主要功能是为请求找到合适的 handler
和 interceptors
,并组合成 HandlerExecutionChain
。查找 handler
的过程通过 getHandlerInternal
方法实现,每个子类都其不同的实现。
所有的 HandlerMapping
的实现都继承于 AbstarctHandlerMapping
, AbstarctHandlerMapping
主要作用是完成拦截器的初始化工作。而通过 AbstarctHandlerMapping
又衍生出两个系列, AbstractUrlHandlerMapping
和 AbstractHandlerMethodMapping
。
AbstractUrlHandlerMapping
也有很多子类的实现,如 SimpleUrlHandlerMapping
、 AbstractDetectingUrlHandlerMapping
。总体来说, AbstractUrlHandlerMapping
需要用到一个保存 url
和 handler
的对应关系的 map
, map
的初始化工作由子类实现。不同的子类会有自己的策略,可以在配置文件中注册,也可以在 spring
容器中找。
AbstractHandlerMethodMapping
系列则通常用于注解的方法,解析包含 @Controller
或者 @RequestMapping
注解的类,建立 url
和 method
的直接对应关系,这也是目前使用最多的一种方式。