AbstractUrlHandlerMapping
是经过url来进行匹配的,也就是说经过url与对应的Handler包存到一个Map中,而后在getHandlerInternal方法中使用url做为key从Map中获取咱们的handler。java
AbstractUrlHandlerMapping
实现了从url获取handler的过程,具体的映射关系,也就是handlerMap则是交给具体子类来去完成的。AbstractUrlHandlerMapping
中定义了handlerMap用来维护映射关系,以下:app
private final Map<String, Object> handlerMap =
new LinkedHashMap<String, Object>();
复制代码
除此以外,还有一个rootHandler,这个用于处理“/”请求。ide
在前面三篇文章中提到过,handler的获取是经过getHandlerInternal方法完成的,下面看下具体的源码,分析下handler的获取和handlerMap的构建。ui
//查找给定请求的URL路径的Handler。
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
//使用lookupPath从Map中查找handler
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
//临时变量,保存原始的handler
Object rawHandler = null;
//是不是‘/’根路径
if ("/".equals(lookupPath)) {
//获取rootHandler
rawHandler = getRootHandler();
}
//若是rawHandler是null
if (rawHandler == null) {
//获取默认的handler
rawHandler = getDefaultHandler();
}
//若是rawHandler不是null
if (rawHandler != null) {
// 若是是string类型,则到容器中查找具体的bean
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
//容器中获取
rawHandler = getApplicationContext().getBean(handlerName);
}
//校验handler和request是否匹配
validateHandler(rawHandler, request);
//注册拦截器
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
//日志debug
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 + "]");
}
//返回handler
return handler;
}
复制代码
在getHandlerInternal
方法中有几个方法调用,像getLookupPathForRequest、getRootHandler、getDefaultHandler、lookupHandler、buildPathExposingHandler等。其中getLookupPathForRequest、getRootHandler、getDefaultHandler这几个没啥好说的;比较核心的就是lookupHandler、buildPathExposingHandler这两个方法。this
lookupHandlerurl
lookupHandler使用getUrlPathHelper().getLookupPathForRequest(request)获取到的lookupPath从Map中查找须要的Handler,一般状况下是直接get不到的。为何呢?缘由在于不少的handler都是使用了Pattern的匹配模式,好比说“/user/*”,星号表示匹配任意内容,并不是是指定url串中的字符。若是Pattern中包含了PathVariable,也不能直接从Map中获取到。spa
除此以外,一个url还可能和多个Pattern相匹配,那么这个时候我们确定就须要选择最优的,因此说查找过程其实并非直接从map中获取那么简单。那么就来看下在lookupHandler中都干了哪些事情:翻译
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// 直接匹配,直接从Map中获取
Object handler = this.handlerMap.get(urlPath);
//取到了
if (handler != null) {
// 若是是string类型,则从容器中获取Bean
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
//验证是否匹配
validateHandler(handler, request);
//注册拦截器
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
// Pattern 匹配,带*号的模式与url进行匹配
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 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);
}
//最佳匹配不为null
if (bestPatternMatch != null) {
//从Map中看看是否有对应的Handler
handler = this.handlerMap.get(bestPatternMatch);
//若是Map中没有
if (handler == null) {
//是否以/结尾
Assert.isTrue(bestPatternMatch.endsWith("/"));
//去除/以后再获取一次
handler = this.handlerMap.get(bestPatternMatch.substring(0, bestPatternMatch.length() - 1));
}
// 若是是String类型,则从容器中获取Bean?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
//验证是否匹配
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);
// 可能有多种最佳模式,让咱们确保咱们有正确的URI模板变量(译)
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;
}
复制代码
上面代码中,关于译注的部分须要说一下;代码以下:debug
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);
}
}
复制代码
以前是经过sort方法进行排序的,而后将第一个做为bestPatternMatch,可是若是多个pattern的顺序相同,也就是说sort返回的是0,存在多种最佳匹配,那就须要确保咱们有正确的URI模板变量。上面代码就是处理这种状况的。日志
buildPathExposingHandler
这个方法在上面的两段代码中都频繁出现,那么这个方法到底有什么做用呢?代码中我注释的是注册拦截器,那么注册的又是什么拦截器?带着这两个问题,咱们来看下代码。
//buildPathExposingHandler为给定的rawHandler构建一个Handler对象,并在执
//行处理程序以前暴露实际的处理程序PATH_WITHIN_HANDLER_MAPPING_ATTRIBUT
//E以及URI_TEMPLATE_VARIABLES_ATTRIBUTE。
//默认实现用一个特殊的拦截器构建一个HandlerExecutionChain,该拦截器暴露
//path属性和uri模板变量。
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;
}
复制代码
四个参数:
从代码注释翻译及代码内容能够了解到,buildPathExposingHandler的做用就是给已经查找到的handler注册两个拦截器
这两个类均是AbstractUrlHandlerMapping
的内部类,也就是两个内部拦截器。这两个拦截器的主要做用就是将与当前url实际匹配的pattern、匹配条件以及url模板参数等设置到request的属性里面去,这样在后面的处理过程当中就能够直接从request属性中获取。看下两个内部类的定义:
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属性
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种设置request属性
exposeUriTemplateVariables(this.uriTemplateVariables, request);
return true;
}
}
复制代码
从内部类的代码能够看出,这两个内部类是经过在preHandle方法中调用exposePathWithinMapping和exposeUriTemplateVariables完成属性设置到request中的。
对于查找handler的关键其实就是维护url和handler的映射关系,也就是handlerMap的构建。在AbstractUrlHandlerMapping
中是经过registerHandler这个方法来构建handlerMap的。AbstractUrlHandlerMapping
提供了两个registerHandler方法,下面就经过代码来看下具体的实现。
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);
}
}
复制代码
第一个registerHandler是将多个url注册到一个处理器。beanName其实就是我们处理器的名称,能够经过beanName到容器中去找到真正的处理器Bean。具体处理就是经过遍历全部的url,而后再经过调用第二个registerHandler将handler注册到handlerMap中。来看第二个:
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;
// 若是的handler是string类型,而且不是lazyInitHandlers,则从SpringMV
//C容器中获取handler
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) {
//异常处理
}
}
else {
//是不是跟路径
if (urlPath.equals("/")) {
if (logger.isInfoEnabled()) {
logger.info("Root mapping to " +
getHandlerDescription(handler));
}
setRootHandler(resolvedHandler);
}
//是不是*模式
else if (urlPath.equals("/*")) {
if (logger.isInfoEnabled()) {
logger.info("Default mapping to " +
getHandlerDescription(handler));
}
setDefaultHandler(resolvedHandler);
}
//加入到handlerMap中
else {
this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isInfoEnabled()) {
logger.info("Mapped URL path [" + urlPath + "] onto " +
getHandlerDescription(handler));
}
}
}
}
复制代码
这个里面首先是看Map中是否有原来传入的url,若是没有就加入,若是有就看下原来保存的和当前注册的handler是不是同一个,若是不是同一个就抛出异常。(同一个url不可能存在两个不一样的handler)。
在put以前,也作了一些“/”和“/*”的验证处理,若是是这两种路径的话就不保存到handlerMap中了。
OK,到这AbstractUrlHandlerMapping
这个类就分析完了,其实AbstractUrlHandlerMapping
作的事情就是定义了一个框子,子类只要完成对Map的初始化就能够了。关于AbstractUrlHandlerMapping
的子类后续再谈。