以前分析過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
的直接對應關係,這也是目前使用最多的一種方式。