之前分析過SpringMVC
中的DispatcherServlet
,分析了SpringMVC
處理請求的過程砸喻。但忽略了一些DispatcherServlet
協(xié)助請求處理的組件川抡,例如SpringMVC
中的HandlerMapping
、HandlerAdapter
返奉、ViewResolvers
等等变逃。
HandlerMappings
HandlerMappings
在DispathServlet
中主要作用是為請求的urlpath
匹配對應(yīng)的Controller
必逆,建立一個映射關(guān)系,根據(jù)請求查找Handler
揽乱、Interceptor
名眉。HandlerMappings
將請求傳遞到HandlerExecutionChain
上,HandlerExecutionChain
包含了一個能夠處理該請求的處理器凰棉,還可以包含攔截改請求的攔截器损拢。
在沒有處理器映射相關(guān)配置情況下,DispatcherServlet
會為你創(chuàng)建一個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
已經(jīng)被放棄了,取代它的是RequestMappingHandlerMapping
绘证,不知道為啥SpringMVC
這個默認配置尚未做修改。
AbstractHandlerMapping
AbstractHandlerMapping
是HandlerMapping
的抽象實現(xiàn)哗讥,是所有HandlerMapping
實現(xiàn)類的父類嚷那。
AbstractHandlerMapping
的作用是是為了初始化Interceptors
。AbstractHandlerMapping
重寫了WebApplicationObjectSupport
的initApplicationContext
方法杆煞。
protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.adaptedInterceptors);
initInterceptors();
}
extendInterceptors
方法魏宽,Springmvc
并沒有做出具體實現(xiàn),這里留下一個拓展决乎,子類可以重寫這個模板方法队询,為子類添加或者修改Interceptors
。detectMappedInterceptors
方法將SpringMVC
容器中所有MappedInterceptor
類的bean
添加到adaptedInterceptors
中构诚。最后調(diào)用
initInterceptors
初始化攔截器蚌斩。遍歷interceptors
將WebRequestInterceptor
和HandlerInterceptor
類型的攔截器添加到adaptedInterceptors
中。
HandlerMapping
通過getHandler
方法來獲取請求的處理器Handler
和攔截器Interceptor
范嘱。在getHandlerExecutionChain
方法中將遍歷之前初始化的adaptedInterceptors
送膳,為當前的請求選擇對應(yīng)的MappedInterceptors
和adaptedInterceptors
。
AbstractUrlHandlerMapping
AbstractUrlHandlerMapping
AbstractUrlHandlerMapping
繼承于AbstractHandlerMapping
丑蛤,它是通過URL
來匹配具體的Handler
叠聋。AbstractUrlHandlerMapping
維護一個handlerMap
來存儲Url
和Handler
的映射關(guān)系。
AbstractUrlHandlerMapping
重寫了AbstractHandlerMapping
類中的getHandlerInternal
方法受裹。HandlerMapping
通過getHandler
方法碌补,就會調(diào)用這里的getHandlerInternal
方法來獲取Handler
。getHandlerInternal
方法中關(guān)鍵調(diào)用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;
}
- 首先調(diào)用
lookupHandler
方法來獲取handler
厦章。在lookupHandler
方法中镇匀,先通過URL
在handlerMap
查找是否有合適的handler
。 - 如果沒有獲取到
handler
闷袒,遍歷handlerMap
利用正則匹配的方法坑律,找到符合要求的handlers
(有可能是多個)。 - 正則匹配是采用
Ant
風格囊骤,將會通過排序篩選出一個匹配程度最高的Handler
晃择。 - 最后調(diào)用
buildPathExposingHandler
方法構(gòu)建一個handler
,添加PathExposingHandlerInterceptor
和UriTemplateVariablesHandlerInterceptor
兩個攔截器并返回。
上面介紹獲取handler
的過程中也物,會先從handlerMap
查找宫屠。下面看一下handlerMap
是如何初始化的。AbstractUrlHandlerMapping
是通過registerHandler
初始化handlerMap
的滑蚯。AbstractUrlHandlerMapping
共有兩個registerHandler
方法浪蹂。分別是注冊多個url
到一個handler
和注冊一個url
到一個handler
。首先判斷handlerMap
是否有此handler
告材。如果存在的話坤次,判斷是否一致,不一致則拋出異常斥赋,如果不存在的話缰猴,如果url
是/
、/*
疤剑,則滑绒,返回root handler
、default handler
隘膘,如果不是將添加到handlerMap
中疑故。
SimpleUrlHandlerMapping
SimpleUrlHandlerMapping
繼承于AbstractUrlHandlerMapping
。SimpleUrlHandlerMapping
重寫了父類AbstractHandlerMapping
中的初始化方法initApplicationContext
弯菊。在initApplicationContext
方法中調(diào)用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都是以/
開頭吨悍,然后依次調(diào)用父類的registerHandler
方法注冊到AbstractUrlHandlerMapping
中的handlerMap
。
在使用SimpleUrlHandlerMapping
時蹋嵌,需要在注冊的時候配置其urlmap
否則會拋異常育瓜。
AbstractDetectingUrlHandlerMapping
AbstractDetectingUrlHandlerMapping
類繼承于AbstractUrlHandlerMapping
類,重寫了initApplicationContext
方法栽烂,在initApplicationContext
方法中調(diào)用了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
,調(diào)用determineUrlsForHandler
方法解析url
焰手,這里的determineUrlsForHandler
也是運用了模板方法設(shè)計模式糟描,具體的實現(xiàn)在其子類中,如果解析到子類书妻,將注冊到父類的handlerMap
中船响。
BeanNameUrlHandlerMapping
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
規(guī)則也很簡單见间,判斷beanName
是否以/
開頭。
BeanNameUrlHandlerMapping
是SpringMVC
的默認映射配置工猜。
AbstractHandlerMethodMapping
通常我們也習慣于用@Controller
米诉、@Re questMapping
來定義Handler
,AbstractHandlerMethodMapping
可以將method
作為Handler
來使用篷帅。
AbstractHandlerMethodMapping
實現(xiàn)了InitializingBean
接口史侣,實現(xiàn)了afterPropertiesSet
方法。當容器啟動的時候會調(diào)用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
掃描應(yīng)用上下文,獲取所有的bean
箭昵。 - 遍歷所有的
beanName
税朴,調(diào)用isHandler
方法判斷是目標bean
是否包含@Controller
或@RequestMapping
注解。 - 對于帶有
@Controller
或@RequestMapping
注解的類宙枷,調(diào)用detectHandlerMethods
委托處理,獲取所有的method
,并調(diào)用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
方法中調(diào)用了子類RequestMappingHandlerMapping
實現(xiàn)了getMappingForMethod
模板方法瘾杭,用于構(gòu)建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
和請求對應(yīng)的RequestMappingInfo
保存到methodMap
中贤笆。
最終detectHandlerMethods
將遍歷這個methodMap
,調(diào)用registerHandlerMethod
注冊HandlerMethod
到MappingRegistry
讨阻。
在AbstractHandlerMethodMapping
類中芥永,有個內(nèi)部類MappingRegistry
,用來存儲mapping
和 handler methods
注冊關(guān)系钝吮,并提供了并發(fā)訪問方法埋涧。
AbstractHandlerMethodMapping
通過getHandlerInternal
來為一個請求選擇對應(yīng)的handler
板辽。
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 根據(jù)request獲取對應(yīng)的urlpath
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
// 獲取讀鎖
this.mappingRegistry.acquireReadLock();
try {
// 調(diào)用lookupHandlerMethod方法獲取請求對應(yīng)的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
的具體實現(xiàn)如下:
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設(shè)為請求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
是個內(nèi)部類劲弦,封裝了匹配條件和handlerMethod
兩個屬性,默認的實現(xiàn)是將lookupPath
設(shè)置為請求的屬性醇坝。
總結(jié)
本文從源碼角度上分析了HandlerMapping
的各種實現(xiàn)邑跪。主要功能是為請求找到合適的handler
和interceptors
,并組合成HandlerExecutionChain
呼猪。查找handler
的過程通過getHandlerInternal
方法實現(xiàn)画畅,每個子類都其不同的實現(xiàn)。
所有的HandlerMapping
的實現(xiàn)都繼承于AbstarctHandlerMapping
郑叠,AbstarctHandlerMapping
主要作用是完成攔截器的初始化工作夜赵。而通過AbstarctHandlerMapping
又衍生出兩個系列,AbstractUrlHandlerMapping
和AbstractHandlerMethodMapping
乡革。
AbstractUrlHandlerMapping
也有很多子類的實現(xiàn)寇僧,如SimpleUrlHandlerMapping
、AbstractDetectingUrlHandlerMapping
沸版∴铱總體來說,AbstractUrlHandlerMapping
需要用到一個保存url
和handler
的對應(yīng)關(guān)系的map
视粮,map
的初始化工作由子類實現(xiàn)细办。不同的子類會有自己的策略,可以在配置文件中注冊蕾殴,也可以在spring
容器中找笑撞。
AbstractHandlerMethodMapping
系列則通常用于注解的方法,解析包含@Controller
或者@RequestMapping
注解的類钓觉,建立url
和method
的直接對應(yīng)關(guān)系茴肥,這也是目前使用最多的一種方式。