以前分析过SpringMVC
中的DispatcherServlet
,分析了SpringMVC
处理请求的过程。但忽略了一些DispatcherServlet
协助请求处理的组件,例如SpringMVC
中的HandlerMapping
、HandlerAdapter
、ViewResolvers
等等。java
HandlerMappings
在DispathServlet
中主要做用是为请求的urlpath
匹配对应的Controller
,创建一个映射关系,根据请求查找Handler
、Interceptor
。HandlerMappings
将请求传递到HandlerExecutionChain
上,HandlerExecutionChain
包含了一个可以处理该请求的处理器,还能够包含拦截改请求的拦截器。web
在没有处理器映射相关配置状况下,DispatcherServlet
会为你建立一个BeanNameUrlHandlerMapping
做为默认映射的配置。在DispatchServlet.properties
文件中对于HandlerMapping
的默认配置是:spring
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
方法。mvc
protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); detectMappedInterceptors(this.adaptedInterceptors); initInterceptors(); }
extendInterceptors
方法,Springmvc
并无作出具体实现,这里留下一个拓展,子类能够重写这个模板方法,为子类添加或者修改Interceptors
。app
detectMappedInterceptors
方法将SpringMVC
容器中全部MappedInterceptor
类的bean
添加到adaptedInterceptors
中。ide
最后调用initInterceptors
初始化拦截器。遍历interceptors
将WebRequestInterceptor
和HandlerInterceptor
类型的拦截器添加到adaptedInterceptors
中。ui
HandlerMapping
经过getHandler
方法来获取请求的处理器Handler
和拦截器Interceptor
。在getHandlerExecutionChain
方法中将遍历以前初始化的adaptedInterceptors
,为当前的请求选择对应的MappedInterceptors
和adaptedInterceptors
。this
AbstractUrlHandlerMapping
继承于AbstractHandlerMapping
,它是经过URL
来匹配具体的Handler
。AbstractUrlHandlerMapping
维护一个handlerMap
来存储Url
和Handler
的映射关系。
AbstractUrlHandlerMapping
重写了AbstractHandlerMapping
类中的getHandlerInternal
方法。HandlerMapping
经过getHandler
方法,就会调用这里的getHandlerInternal
方法来获取Handler
。getHandlerInternal
方法中关键调用lookupHandler
方法去获取handler
。
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception { // 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) throws BeansException { 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() throws BeansException { 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(final Object 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 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); } }
selectMethods
方法中重写了MetadataLookup
中的inspect
方法,inspect
方法中调用了子类RequestMappingHandlerMapping
实现了getMappingForMethod
模板方法,用于构建RequestMappingInfo
。
public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<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
。
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { // 根据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
的具体实现以下:
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { 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
的直接对应关系,这也是目前使用最多的一种方式。