SpringMVC源码解读--HandlerMapping代码解读
Posted 低调的洋仔
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringMVC源码解读--HandlerMapping代码解读相关的知识,希望对你有一定的参考价值。
AbstractHandlerMapping
AbstractHandlerMapping这个类,在父类进行设置容器的时候,就会被调用到的。那么调用的是
/**
* Initializes the interceptors.
* @see #extendInterceptors(java.util.List)
* @see #initInterceptors()
*/
@Override
protected void initApplicationContext() throws BeansException
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.mappedInterceptors);
initInterceptors();
/**
* Detect beans of type @link MappedInterceptor and add them to the list of mapped interceptors.
* <p>This is called in addition to any @link MappedInterceptors that may have been provided
* via @link #setInterceptors, by default adding all beans of type @link MappedInterceptor
* from the current context and its ancestors. Subclasses can override and refine this policy.
* @param mappedInterceptors an empty list to add @link MappedInterceptor instances to
*/
protected void detectMappedInterceptors(List<MappedInterceptor> mappedInterceptors)
mappedInterceptors.addAll(
BeanFactoryUtils.beansOfTypeIncludingAncestors(
getApplicationContext(), MappedInterceptor.class, true, false).values());
initInterceptors主要是放了两种的拦截器进来,一种是mappedInterceptors另一种是adaptedInterceptors,然后mappedInterceptors类型的拦截器的话只能是匹配用户请求的url才能放到用户的请求的拦截链子中去,但是后面这个adaptedInterceptors的话就可以直接放进去的。
/**
* Initialize the specified interceptors, checking for @link MappedInterceptors and
* adapting @link HandlerInterceptors and @link WebRequestInterceptors if necessary.
* @see #setInterceptors
* @see #adaptInterceptor
*/
protected void initInterceptors()
if (!this.interceptors.isEmpty())
for (int i = 0; i < this.interceptors.size(); i++)
Object interceptor = this.interceptors.get(i);
if (interceptor == null)
throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
if (interceptor instanceof MappedInterceptor)
this.mappedInterceptors.add((MappedInterceptor) interceptor);
else
this.adaptedInterceptors.add(adaptInterceptor(interceptor));
真正使用的过程中一般是使用getHandler来获取拦截器和处理器的handler的。
/**
* Look up a handler for the given request, falling back to the default
* handler if no specific one is found.
* @param request current HTTP request
* @return the corresponding handler instance, or the default handler
* @see #getHandlerInternal
*/
@Override
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);
return getHandlerExecutionChain(handler, request);
/**
* Build a @link HandlerExecutionChain for the given handler, including
* applicable interceptors.
* <p>The default implementation builds a standard @link HandlerExecutionChain
* with the given handler, the handler mapping's common interceptors, and any
* @link MappedInterceptors matching to the current request URL. Subclasses
* may override this in order to extend/rearrange the list of interceptors.
* <p><b>NOTE:</b> The passed-in handler object may be a raw handler or a
* pre-built @link HandlerExecutionChain. This method should handle those
* two cases explicitly, either building a new @link HandlerExecutionChain
* or extending the existing chain.
* <p>For simply adding an interceptor in a custom subclass, consider calling
* @code super.getHandlerExecutionChain(handler, request) and invoking
* @link HandlerExecutionChain#addInterceptor on the returned chain object.
* @param handler the resolved handler instance (never @code null)
* @param request current HTTP request
* @return the HandlerExecutionChain (never @code null)
* @see #getAdaptedInterceptors()
*/
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request)
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
chain.addInterceptors(getAdaptedInterceptors());
// 这里将刚才设置的mappedInterceptors中的url进行匹配,就是上面提到的部分了。
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (MappedInterceptor mappedInterceptor : this.mappedInterceptors)
if (mappedInterceptor.matches(lookupPath, this.pathMatcher))
chain.addInterceptor(mappedInterceptor.getInterceptor());
return chain;
// 这里将刚才设置的mappedInterceptors中的url进行匹配,就是上面提到的部分了。
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (MappedInterceptor mappedInterceptor : this.mappedInterceptors)
if (mappedInterceptor.matches(lookupPath, this.pathMatcher))
chain.addInterceptor(mappedInterceptor.getInterceptor());
return chain;
AbstractURLHandlerMapping
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping
大致的原理是将url与handler保存在一个Map里面。
/**
* Look up a handler for the URL path of the given request.
* @param request current HTTP request
* @return the handler instance, or @code null if none found
*/
@Override
protected Object getHandlerInternal(HttpServletRequest request) throws Exception
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
Object handler = lookupHandler(lookupPath, request);
if (handler == null)
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
Object rawHandler = null;
if ("/".equals(lookupPath))
rawHandler = getRootHandler();// 如果是/的话就默认用了RootHandler了。
if (rawHandler == null)
rawHandler = getDefaultHandler();// 如果是空的话就默认的handler。
if (rawHandler != null)
// Bean name or resolved handler?
if (rawHandler instanceof String)
String handlerName = (String) rawHandler;
rawHandler = getApplicationContext().getBean(handlerName);
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
if (handler != null && logger.isDebugEnabled())
logger.debug("Mapping [" + lookupPath + "] to " + handler);
else if (handler == null && logger.isTraceEnabled())
logger.trace("No handler mapping found for [" + lookupPath + "]");
return handler;
LookUpHandler
如果能直接匹配到的话,就直接将匹配的返回了就好了。
但是呢,可能并没有匹配到,那就用正则匹配,然后来获取能够匹配到的handler,它会对其进行排序的,然后排序完之后,挑选第一个,但是,可能并不是只有一个的得分情况是第一个的那个值,也就是说可能存在多个url实际上和第一个的url的得分情况是一样的,那么就应该扫描后续的那些url部分,然后将她们放在了uriTemplateVariables里面。
/**
* Look up a handler instance for the given URL path.
* <p>Supports direct matches, e.g. a registered "/test" matches "/test",
* and various Ant-style pattern matches, e.g. a registered "/t*" matches
* both "/test" and "/team". For details, see the AntPathMatcher class.
* <p>Looks for the most exact pattern, where most exact is defined as
* the longest path pattern.
* @param urlPath URL the bean is mapped to
* @param request current HTTP request (to expose the path within the mapping to)
* @return the associated handler instance, or @code null if not found
* @see #exposePathWithinMapping
* @see org.springframework.util.AntPathMatcher
*/
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);
String bestPatternMatch = 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);
bestPatternMatch = matchingPatterns.get(0);
if (bestPatternMatch != null)
handler = this.handlerMap.get(bestPatternMatch);
// Bean name or resolved handler?
if (handler instanceof String)
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, 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(bestPatternMatch, 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, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
// No handler found...
return null;
buildPathExposingHandler
/**
* Build a handler object for the given raw handler, exposing the actual
* handler, the @link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, as well as
* the @link #URI_TEMPLATE_VARIABLES_ATTRIBUTE before executing the handler.
* <p>The default implementation builds a @link HandlerExecutionChain
* with a special interceptor that exposes the path attribute and uri template variables
* @param rawHandler the raw handler to expose
* @param pathWithinMapping the path to expose before executing the handler
* @param uriTemplateVariables the URI template variables, can be @code null if no variables found
* @return the final handler object
*/
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
String pathWithinMapping, Map<String, String> uriTemplateVariables)
HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
if (!CollectionUtils.isEmpty(uriTemplateVariables))
chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
return chain;
这里的俩拦截器本身就是用于将当前的Url实际匹配的Pattern、匹配条件,和URL模版参数设置到Request请求中去。这样以后就可以从url中获取了。
private class PathExposingHandlerInterceptor extends HandlerInterceptorAdapter
private final String bestMatchingPattern;
private final String pathWithinMapping;
public PathExposingHandlerInterceptor(String bestMatchingPattern, String pathWithinMapping)
this.bestMatchingPattern = bestMatchingPattern;
this.pathWithinMapping = pathWithinMapping;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
exposePathWithinMapping(this.bestMatchingPattern, this.pathWithinMapping, request);
request.setAttribute(HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING, supportsTypeLevelMappings());
return true;
private class UriTemplateVariablesHandlerInterceptor extends HandlerInterceptorAdapter
private final Map<String, String> uriTemplateVariables;
public UriTemplateVariablesHandlerInterceptor(Map<String, String> uriTemplateVariables)
this.uriTemplateVariables = uriTemplateVariables;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
exposeUriTemplateVariables(this.uriTemplateVariables, request);
return true;
总结下:
从DespatcherServelet类中的getHandler开始进行调用,然后调用了AbstractHandlerMapping中的getHandler方法,然后里面有个getHandlerInternal方法,这个方法要求子类进行实现的,那么,在其子类中AbstractUrlHandlerMapping中,实现了这个方法,其中比较重要的是lookupHandler方法,然后这个方法会获取handlerMap中的url的映射对象,如果没有的话就说明直接获取不到,只能模式匹配通过url的匹配确定一个集合,然后排序,然后将排序结果选取第一个返回,但是也会有多个价值同等的情况,那么这些同等的元素将会全部封装进一个集合中去,然后这个结合连同刚才匹配到的第一个结果,通过封装进一个执行ExecutionChain中去,然后层层返回,在getHandler接收到返回值之后还要检查是不是string类型的,如果是的话就尝试获取该字符串对应的对象的实例,到这里确保了一个比较重要的概念,是handler到底是谁的问题,之前是各种字符串的handler其实是url的,但是为了映射成能够被调用的handler也就是controller等的类的实例,只能getBean的形式来从容器中获取类的实例了,最后将返回的结果封装称为执行链,但是这里需要注意的是getHandlerInternal返回的handler是经过处理以后的,有可能会已经封装为chain了,所以在最后封装成chain链的时候会进行一个确认,到底需要不需要自己进一步封装了。
里面这个handlerMap子类会自动的进行注册的。
AbstractUrlHandlerMapping
/**
* Register the specified handler for the given URL paths.
* @param urlPaths the URLs that the bean should be mapped to
* @param beanName the name of the handler bean
* @throws BeansException if the handler couldn't be registered
* @throws IllegalStateException if there is a conflicting handler registered
*/
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException
Assert.notNull(urlPaths, "URL path array must not be null");
for (String urlPath : urlPaths)
registerHandler(urlPath, beanName);
/**
* Register the specified handler for the given URL path.
* @param urlPath the URL the bean should be mapped to
* @param handler the handler instance or handler bean name String
* (a bean name will automatically be resolved into the corresponding handler bean)
* @throws BeansException if the handler couldn't be registered
* @throws IllegalStateException if there is a conflicting handler registered
*/
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
Object resolvedHandler = handler;
// Eagerly resolve handler if referencing singleton via name.
if (!this.lazyInitHandlers && handler instanceof String)
String handlerName = (String) handler;
if (getApplicationContext().isSingleton(handlerName))
resolvedHandler = getApplicationContext().getBean(handlerName);
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null)
if (mappedHandler != resolvedHandler)
throw new IllegalStateException(
"Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
else
if (urlPath.equals("/"))
if (logger.isInfoEnabled())
logger.info("Root mapping to " + getHandlerDescription(handler));
setRootHandler(resolvedHandler);// 如果是斜杠就设置为roothandler
else if (urlPath.equals("/*"))
if (logger.isInfoEnabled())
logger.info("Default mapping to " + getHandlerDescription(handler));
setDefaultHandler(resolvedHandler);// 如果是/*就设置为defaultHandler
else
this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isInfoEnabled())
logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
上面的子类调用,注册使用的。
SimpleUrlHandlerMapping
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping
调用了一个父类的initApplicationContext,然后调用registerHandlers方法,来完成注册handler,然后调用的就是父类的registerHandler方法了、
/**
* Calls the @link #registerHandlers method in addition to the
* superclass's initialization.
*/
@Override
public void initApplicationContext() throws BeansException
super.initApplicationContext();
registerHandlers(this.urlMap);
/**
* Register all handlers specified in the URL map for the corresponding paths.
* @param urlMap Map with URL paths as keys and handler beans or bean names as values
* @throws BeansException if a handler couldn't be registered
* @throws IllegalStateException if there is a conflicting handler registered
*/
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);
AbstractDetectingUrlHandlerMapping
/**
* Calls the @link #detectHandlers() method in addition to the
* superclass's initialization.
*/
@Override
public void initApplicationContext() throws ApplicationContextException
super.initApplicationContext();
detectHandlers();
/**
* Register all handlers found in the current ApplicationContext.
* <p>The actual URL determination for a handler is up to the concrete
* @link #determineUrlsForHandler(String) implementation. A bean for
* which no such URLs could be determined is simply not considered a handler.
* @throws org.springframework.beans.BeansException if the handler couldn't be registered
* @see #determineUrlsForHandler(String)
*/
protected void detectHandlers() throws BeansException
if (logger.isDebugEnabled())
logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
// 获取容器中的所有bean的名字
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
// 对每个beanName解析url,如果能解析到的话就注册到父类的map中。
// 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");
// 对每个beanName解析url,如果能解析到的话就注册到父类的map中。
// 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");
BeanNameUrlHandlerMapping
public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping
/**
* Checks name and aliases of the given bean for URLs, starting with "/".
*/
@Override
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和alias是不是以/开头,如果是的话就将其作为url.
AbstractControllerUrlHandlerMapping
/**
* This implementation delegates to @link #buildUrlsForHandler,
* provided that @link #isEligibleForMapping returns @code true.
*/
@Override
protected String[] determineUrlsForHandler(String beanName)
Class<?> beanClass = getApplicationContext().getType(beanName);
if (isEligibleForMapping(beanName, beanClass))
return buildUrlsForHandler(beanName, beanClass);
else
return null;
将实现了Controller或者是标记了Controller注解的类的bean作为handler,并且可以通过设置excludedClasses和excludedPackages将不包含的bean或者不包含的包下的所有bean排除在外。
protected boolean isEligibleForMapping(String beanName, Class<?> beanClass)
if (beanClass == null)
if (logger.isDebugEnabled())
logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " +
"because its bean type could not be determined");
return false;
if (this.excludedClasses.contains(beanClass))
if (logger.isDebugEnabled())
logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " +
"because its bean class is explicitly excluded: " + beanClass.getName());
return false;
String beanClassName = beanClass.getName();
for (String packageName : this.excludedPackages)
if (beanClassName.startsWith(packageName))
if (logger.isDebugEnabled())
logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " +
"because its bean class is defined in an excluded package: " + beanClass.getName());
return false;
return isControllerType(beanClass);// 是不是controller类型
ControllerClassNameHandlerMapping和ControllerBeanNameHandlerMapping,一个是使用了beanName作为url一个是使用的className作为的url.
AbstractHandlerMethodMapping
因为他实现了一个InitializingBean所以会自动调用其
/**
* Detects handler methods at initialization.
*/
@Override
public void afterPropertiesSet()
initHandlerMethods();
这个方法,然后调用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) &&
isHandler(getApplicationContext().getType(beanName)))
detectHandlerMethods(beanName);
handlerMethodsInitialized(getHandlerMethods());
detectHandlerMethods方法
protected void detectHandlerMethods(final Object handler)
Class<?> handlerType =
(handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
// Avoid repeated calls to getMappingForMethod which would rebuild RequestMappingInfo instances
final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
final Class<?> userType = ClassUtils.getUserClass(handlerType);
Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter()
@Override
public boolean matches(Method method)
T mapping = getMappingForMethod(method, userType);
if (mapping != null)
mappings.put(method, mapping);
return true;
else
return false;
);
for (Method method : methods) // 将符合要求的handler保存到三个map中去。
registerHandlerMethod(handler, method, mappings.get(method));
/**
* Register a handler method and its unique mapping.
* @param handler the bean name of the handler or the handler instance
* @param method the method to register
* @param mapping the mapping conditions associated with the handler method
* @throws IllegalStateException if another method was already registered
* under the same mapping
*/
protected void registerHandlerMethod(Object handler, Method method, T mapping)
HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);
if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) //检查是不是已经存在了,如果已经存在了的话就看是不是一样的,如果不一样要抛出异常的。
throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean() +
"' bean method \\n" + newHandlerMethod + "\\nto " + mapping + ": There is already '" +
oldHandlerMethod.getBean() + "' bean method\\n" + oldHandlerMethod + " mapped.");
// 添加handlermethods
this.handlerMethods.put(mapping, newHandlerMethod);
if (logger.isInfoEnabled())
logger.info("Mapped \\"" + mapping + "\\" onto " + newHandlerMethod);
Set<String> patterns = getMappingPathPatterns(mapping);
for (String pattern : patterns)
if (!getPathMatcher().isPattern(pattern))
this.urlMap.add(pattern, mapping);// 添加urlMap中
if (this.namingStrategy != null)
String name = this.namingStrategy.getName(newHandlerMethod, mapping);
updateNameMap(name, newHandlerMethod);// 添加updateNameMap中、
// 添加handlermethods
this.handlerMethods.put(mapping, newHandlerMethod);
if (logger.isInfoEnabled())
logger.info("Mapped \\"" + mapping + "\\" onto " + newHandlerMethod);
Set<String> patterns = getMappingPathPatterns(mapping);
for (String pattern : patterns)
if (!getPathMatcher().isPattern(pattern))
this.urlMap.add(pattern, mapping);// 添加urlMap中
if (this.namingStrategy != null)
String name = this.namingStrategy.getName(newHandlerMethod, mapping);
updateNameMap(name, newHandlerMethod);// 添加updateNameMap中、
在AbstractHandlerMapping中实际上在调用getHandler的时候调用了一个getInternalHandler方法,
这个方法在AbstractHandlerMethodMapping中也进行了实现。
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled())
logger.debug("Looking up handler method for path " + lookupPath);
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);
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception
List<Match> matches = new ArrayList<Match>();
List<T> directPathMatches = this.urlMap.get(lookupPath);
if (directPathMatches != null)
addMatchingMappings(directPathMatches, matches, request);
if (matches.isEmpty())
// No choice but to go through all mappings...
addMatchingMappings(this.handlerMethods.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)
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 // 无法使用lookupPath得到匹配条件,将所有的匹配条件加入matches中去。
return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
RequestMappingHandlerMapping
/**
* Uses method and type-level @@link RequestMapping annotations to create
* the RequestMappingInfo.
* @return the created RequestMappingInfo, or @code null if the method
* does not have a @code @RequestMapping annotation.
* @see #getCustomMethodCondition(Method)
* @see #getCustomTypeCondition(Class)
*/
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType)
RequestMappingInfo info = null;
RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (methodAnnotation != null)
RequestCondition<?> methodCondition = getCustomMethodCondition(method);
info = createRequestMappingInfo(methodAnnotation, methodCondition);
RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
if (typeAnnotation != null)
RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
return info;
根据@RequestMapping来找方法的,如果没有注解的,就返回null了。
总结一下:
如何匹配的方法呢?
封装,将方法封装成一个RequestMappingInfo类,里面封装了多个的相关的方法标记中可能会有的参数。以次来标识一个方法。
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo>
private final String name;
private final PatternsRequestCondition patternsCondition;
private final RequestMethodsRequestCondition methodsCondition;
private final ParamsRequestCondition paramsCondition;
private final HeadersRequestCondition headersCondition;
private final ConsumesRequestCondition consumesCondition;
private final ProducesRequestCondition producesCondition;
private final RequestConditionHolder customConditionHolder;
整个Method类型的HandlerMapping的话主要的流程如下:
首先是AbstractHandlerMethodMapping类实现了InitilizingBean接口,那么会现调用afterPropertiesSet来设置其参数,调用initMethodHandler方法,然后这个方法中会调用detectHandlerMethod方法,这个方法会将handler保存在Map中去,
protected void detectHandlerMethods(final Object handler)
Class<?> handlerType =
(handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
// Avoid repeated calls to getMappingForMethod which would rebuild RequestMappingInfo instances
final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
final Class<?> userType = ClassUtils.getUserClass(handlerType);
Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter()
@Override
public boolean matches(Method method)
T mapping = getMappingForMethod(method, userType);
if (mapping != null)
mappings.put(method, mapping);// 筛选出来能够进行匹配的方法添加到这个map中去。
return true;
else
return false;
);
for (Method method : methods)
registerHandlerMethod(handler, method, mappings.get(method));// 进行相应的注册
注册分为三个不同的map
一个handlerMap保存的是匹配条件和方法的映射关系。
一个urlMap保存的是url和方法的映射关系。
一个nameMap是保存的方法名和MethodHandler的对应关系
后面这俩属于多值的情况。
protected void registerHandlerMethod(Object handler, Method method, T mapping)
HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);
if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod))
throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean() +
"' bean method \\n" + newHandlerMethod + "\\nto " + mapping + ": There is already '" +
oldHandlerMethod.getBean() + "' bean method\\n" + oldHandlerMethod + " mapped.");
this.handlerMethods.put(mapping, newHandlerMethod);
if (logger.isInfoEnabled())
logger.info("Mapped \\"" + mapping + "\\" onto " + newHandlerMethod);
Set<String> patterns = getMappingPathPatterns(mapping);
for (String pattern : patterns)
if (!getPathMatcher().isPattern(pattern))
this.urlMap.add(pattern, mapping);
if (this.namingStrategy != null)
String name = this.namingStrategy.getName(newHandlerMethod, mapping);
updateNameMap(name, newHandlerMethod);
上面完成一个初始化的过程,然后调用过程中会调用其handlerInternal方法
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled())
logger.debug("Looking up handler method for path " + lookupPath);
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);
这个方法里面主要是lookupHandlerMethod方法了。
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception
List<Match> matches = new ArrayList<Match>();
List<T> directPathMatches = this.urlMap.get(lookupPath);// 根据url获取可以进行匹配的method
if (directPathMatches != null) // 将匹配的条件添加到matches中去
addMatchingMappings(directPathMatches, matches, request);
if (matches.isEmpty()) // 没有匹配条件
// No choice but to go through all mappings...所有的匹配条件全部添加进去
addMatchingMappings(this.handlerMethods.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) // 前两个相同抛出异常
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(handlerMethods.keySet(), lookupPath, request);
再总之:
整个的流程基本上就是先初始化基本数据,首先在AbstractHandlerMapping中会默认初始化一些拦截器,然后也与部分请求的路径等进行匹配,一旦匹配成功可以直接添加上,不过也存在不用匹配直接添加的情况。
AbstractUrlHandlerMapping这个是针对Url进行匹配的,主要的目的是找到Controller的类,它是根据AbstractHandlerMapping中的方法,getHandler方法也是这种类的外界访问切入口来调用的,内部访问了getHandlerInternal方法,然后这个方法会调用lookuppath寻找能够进行匹配的url对应的controller,然后在匹配的过程中如果直接能匹配就返回,不能的话就采用模式匹配,然后匹配结果再排个序,然后获取第零个作为返回的。不过如果存在多个和第零个价值一样的也会将其封装进一个chain中返回,这个chain是作为多重情况返回的,返回的结果回到父类AbstractHandlerMapping的getHandler中去,然后封装刚刚返回的数据称为一个chain链。然后直接作为结果返回就好了。
需要注意的是,在获取url与controller等的实例之间的关系时,不使用父类去挨着遍历的,而是在子类中自动注册到父类中的。所以AbstractUrlHandlerMapping中会有一个handlerMap的集合装有所以的映射关系。
AbstractMethodHandlerMapping这个是针对method来进行匹配的,
初始化过程中是因为实现了initilizingBean接口,然后会调用afterPropertiesSet方法,这个方法调用了initHandlerMethods方法,在里面会获取到所有的bean对象,然后又通过循环的方式调用了detectHandlerMethods(beanName)方法,这个方法会对类中的放进行选取,然后将选取到的符合条件的方法用于注册三个map,其实会用到controller和method和mappings.get(method).获取到的这个集合通过循环遍历的方式进行注册,会注册到handlerMap和urlMap和nameMap中去,到这里注册完成后基本上完成了初始化的过程。
调用的过程中实际上是调用了getHandlerInternal方法的,这个方法中会调用lookupHandlerMethod方法来对方法进行筛选,能匹配到的都放到一个集合里面去,然后对其进行排序,选取排序后的第一个作为最好的,如果还有第二个和第一个是一样的话就说明冲突了,抛出异常,然后将最好的这个匹配结果封装成methodResolver返回去,翻回去也就是返回到了getHandler中去了,那么接下来将得到的method的handler进行封装,将其封装为ExecutionChain然后返回即可完成了。
以上是关于SpringMVC源码解读--HandlerMapping代码解读的主要内容,如果未能解决你的问题,请参考以下文章
SpringMVC源码解读 - HandlerMapping
SpringMVC源码解读 - RequestMapping注解实现解读 - RequestMappingInfo
SpringMVC源码解读 - RequestMapping注解实现解读 - RequestCondition体系
SpringMVC源码解读--HandlerMapping代码解读