java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringMVC的HandlerMapping

深度理解SpringMVC中的HandlerMapping

作者:Olge

这篇文章主要介绍了深度理解SpringMVC中的HandlerMapping,HandlerMapping的作用根据request找到对应的处理器Handler,在HandlerMapping接口中有一个唯一的方法getHanler,需要的朋友可以参考下

HandlerMapping的初始化策略

HandlerMapping 的作用根据 request 找到对应的处理器 Handler ,在 HandlerMapping 接口中有一个唯一的方法 getHanler , HandlerMapping 接口的定义如下:

public interface HandlerMapping {
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

方法的实现非常灵活,只要能根据 request 返回 HandlerExecutionChain 就可以了。

SpringMVC内置了很多 HandlerMapping 的实现类,主体结构如下图所示:

这里写图片描述

从图中可以看出, AbstractHandlerMapping 是 HandlerMapping 的最顶层抽象实现,在 AbstractHandlerMapping 中定义了 HandlerMapping 的整体结构,子类只需要通过默认方法提供初始值或具体的算法即可。

HandlerMapping 是SpringMVC处理请求过程中的一个重要组件,在SpringMVC启动时对容器中的 HandlerMapping 进行初始化,初始化的位置在 DispatcherServlet 的 onRefresh() 方法中:

@Override
protected void onRefresh(ApplicationContext context) {
                 //初始化组件策略
        initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        //初始化HandlerMapping
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
}
private void initHandlerMappings(ApplicationContext context) {
        this.handlerMappings = null;
        if (this.detectAllHandlerMappings) {
            // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
        //从Spring上下文中获取HandlerMapping类型的Bean
            Map<String, HandlerMapping> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
                // We keep HandlerMappings in sorted order.
                OrderComparator.sort(this.handlerMappings);
            }
        }
        else {
            try {
            //获取名称为handlerMapping的bean
                HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
                this.handlerMappings = Collections.singletonList(hm);
            }
            catch (NoSuchBeanDefinitionException ex) {
                // Ignore, we'll add a default HandlerMapping later.
            }
        }
        // Ensure we have at least one HandlerMapping, by registering
        // a default HandlerMapping if no other mappings are found.
        if (this.handlerMappings == null) {
        //初始化默认的HandlerMapping,即BeanNameUrlHandlerMapping
            this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
            if (logger.isDebugEnabled()) {
                logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
            }
        }
    }

从上面的代码中可以很清除的看见SpringMVC对HandlerMapping的初始化过程:

1.首先,根据detectAllHandlerMappings判断是否从容器中查找所有实现了HandlerMapping的bean。detectAllHandlerMappings默认为true,可以通过DispatcherServlet的初始化参数来修改默认值

<init-param>
   <param-name>detectAllHandlerMappings</param-name>
   <param-value>false</param-value>
</init-param>

2.如果detectAllHandlerMappings为false,则从容器中查找明为handlerMapping的bean作为HandlerMapping的唯一实例。

3.如果以上两步都没有找到合适的handlerMapping,则会初始化默认的BeanNameUrlHandlerMapping

以上,是SpringMVC对HandlerMapping的初始化策略,下面在看一下HandlerMapping自身的初始化过程。

HandlerMapping自身的初始化过程

如上面所说, AbstractHandlerMapping 定义了HandlerMapping的整体结构,所以,HandlerMapping的初始化过程也在 HandlerMapping中定义 , AbstractHandlerMapping 继承了 WebApplicationObjectSupport , AbstractHandlerMapping 的创建过程就是在 initApplicationContext 中实现的。

@Override
protected void initApplicationContext() throws BeansException {
        extendInterceptors(this.interceptors);
        detectMappedInterceptors(this.mappedInterceptors);
        initInterceptors();
}

在说明该方法前,先来看一下在 AbstractHandlerMapping 中定义的三个与Interceptor有关的变量:

private final List<Object> interceptors = new ArrayList<Object>();
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<HandlerInterceptor>();
private final List<MappedInterceptor> mappedInterceptors = new ArrayList<MappedInterceptor>();

下面在看一下这三个方法:

protected void extendInterceptors(List<Object> interceptors) {
    }

extendInterceptors 方法用于给子类提供初始化interceptor的入口。

protected void detectMappedInterceptors(List<MappedInterceptor> mappedInterceptors) {
        mappedInterceptors.addAll(
                BeanFactoryUtils.beansOfTypeIncludingAncestors(
                        getApplicationContext(), MappedInterceptor.class, true, false).values());
    }

detectMapedInterceptors 方法从容器中查找所有 Mapped Interceptor 类型的bean,并放入 mappedInterceptors 中。

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));
                }
            }
        }
    }

initInterceptors 方法将 interceptors 里面所有包含的对象按照类型添加到 mappedInterceptors 或 adaptedInterceptors

总结一下:

HandlerMapping的初始化始于WebContextObjectSupport的initApplicationContext方法,这个方法的作用的初始化interceptor,interceptor主要分为两种,一种的mappedInterceptors,另一种是adaptedInterceptors,其中mappedInterptors在应用是要跟url进行匹配,而adapterInterceptors会被直接应用。

HandlerMapping 的入口方法是 getHandler() , 在AbstractHandlerMapping 中,对这个方法进行了实现,同时,也是定义 getHandler() 的主体逻辑,下面看一下:

@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);
}

在 getHandler() 中,做了两件事,第一是根据 request 获取 heandler ,这是HandlerMapping的主要作用, 由getHandlerInternal() 完成

protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;

但该方法是抽象方法,由子类实现。 getHandler() 做的第二件事是将 Handler 和 Interceptor 组装成 HandlerExecutionChain 。

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
        HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
                (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
        chain.addInterceptors(getAdaptedInterceptors());
        String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
        for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) {
            if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                chain.addInterceptor(mappedInterceptor.getInterceptor());
            }
        }
        return chain;
}

在这个方法中,首先初始化一个 HandlerExecutionChain ,然后将所有adaptedInterceptor添加到 HandlerExecutionChain ,最后,根据url从 mappedInterceptors 中过滤出需要的Interceptor并放入 HandlerExecutionChain 中。

总结一下:

应用HandlerMapping的入口方法是getHandler&#xff0c;AbstractionHandlerMapping定义了getHandler的整体结构&#xff1a;1.获取Handler&#xff0c;这一步交给子类完成2.组装hander和interceptor为HandlerExecutionChain所有的adaptedInterceptor会被方法到HandlerExecutionChain&#xff0c;与url匹配的mappedInterceptor会被放入到HandlerExecutionChain中。

上面说的是 AbstractHandlerMapping 的初始化过程和 HandlerMapping 的使用入口的逻辑。

下面详述一下 HandlerMapping 的一个大分支: AbstractUrlHandlerMapping 。

AbstractUrlHandlerMapping

AbstractUrlHandlerMapping 的大致原理是实现从url到 Handler 的映射,映射关系存在在一个map中

所以, AbstractHandlerMapping 的主要工作就是:

1.初始化这个存放映射关系的map

2.从map中找到对应的 Handler 作为 getHandlerInternal() 的返回值。

先来看一下 AbstractUrlHandlerMapping 中对 getHandlerInternal() 的定义:

private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();
@Override
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        //跟url从handlerMap中找到Handler
        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();
            }
            if (rawHandler == null) {
                rawHandler = getDefaultHandler();
            }
            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);
            }
        }
        return handler;
}

总结一个:

AbstractUrlHandlerMapping的getHandlerInternal()主要做了两件事&#xff1a;1.根据url找到Handler2.位Handler设置两个内置拦截器&#xff0c;作用是将当前url匹配的pattern、匹配条件和url模板参数设置到request作用域中

在根据url获取handler的过程中,其实并不是简单是getByKey(url),在编写 Controller 时,通常我们会通过模式匹配或者路径变量来映射url,此时就需要更复杂的匹配过程,来看一下 lookupHandler() 是如何实现的:

protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
        // 首先,直接根据url从handlerMap中获取
        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);
        }
        // 如果没有获取到,按照模式匹配来找
        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);
            //匹配过程中,可以有多个Handler,那么通过排序后,取优先级最高的一个
            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
            //此处处理匹配出多个且优先级相同的handler的情况
            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);
                }
            }
            return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
        }
        // No handler found...
        return null;
}

总结一下:

在根据url获取Handler的过程中

1.首先,直接根据url从handlerMap中获取Handler

2.如果没有找到,按照模式匹配来找到合适的Handler

3.最后,呼应getHandlerInternal()方法,设置两个内置的Interceptor

lookupHandler()返回的实际是HandlerExecutionChain,这也就与AbstractHandlerMapping中getExecutionChain的逻辑相呼应:

HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
                (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
**首先判断getHandlerInternal()方法返回的是否是一个HandlerExecutionChain,如果不是则创建。**      

对于 buildPathExposingHandler() 方法就是完成上面说的设置两个内置拦截器的功能,具体逻辑很简单,就不说了。

在应用上面的逻辑查找 Handler 之前,一个重要的工作是对handlerMap进行初始化。handlerMap的初始化方法定义在 AbstractUrlHandlerMapping 中,单具体的调用是由子类完成的。两个方法如下:

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);
        }
}
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);
            }
            else if (urlPath.equals("/*")) {
                if (logger.isInfoEnabled()) {
                    logger.info("Default mapping to " + getHandlerDescription(handler));
                }
                setDefaultHandler(resolvedHandler);
            }
            else {
                this.handlerMap.put(urlPath, resolvedHandler);
                if (logger.isInfoEnabled()) {
                    logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
                }
            }
        }
}

很简单,思路就是第一个方法调用第二个方法,第一个方法是将多个url映射到一个 Handler ,另外需要注意的是,在 registHandler 中,对 / 和 /* 做了特殊处理,这两个路径会被映射到 rootHandler 上。

以上就是 AbstractUrlHandlerMapping的主体逻辑 ,总结一下:

AbstractHandlerMapping做了两件事:

1.提供初始化handlerMap的入口-registerHander(),这也是与子类交互的接口。

2.根据url从handlerMap中查找对应的Handler,此处需要注意的是AbstractUrlHandlerMapping对根路径做了特殊处理-映射到了rooterHandler。

SimpleUrlHandlerMapping

SimpleUrlHandlerMapping 是 AbstractUrlHanderMapping 的一个子类,实现了从url到 Controller 的映射。

由上文可知, AbstractUrlHandlerMapping 与 SimpleUrlHandlerMapping 的交互接口是 registerHandler ,所以,在 SimpleUrlHandlerMapping 中定义了一个urlMap,这个map的作用是保存从url到 Controller 的配置信息。

private final Map<String, Object> urlMap = new HashMap<String, Object>();

然后,、 `SimpleUrlHandlerMapping 实现了 WebApplicationObjectSupport ,并覆写了 initApplicationContext() 方法

@Override
public void initApplicationContext() throws BeansException {
        super.initApplicationContext();
        registerHandlers(this.urlMap);
}

可以看出,首先调用父类方法完成对Interceptor的处理,然后调用了 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);
            }
        }
}

从上面的代码可以看出, SimpleUrlHandlerMapping 的 registerHandlers 方法主要是对配置进来的数据进行简单处理,然后调用了父类的 registerHandler 方法。

AbstractHandlerMethodMapping

AbstractHandlerMethodMapping 是与 AbstractUrlHandlerMapping 平行了另一种 HandlerMapping ,将方法作为处理器 Handler ,SpringMVC用 HandlerMethod 来表示这个类型的 Handler 。

从 HandlerMapping 的结构图中可以看出,此类一共包含三个类: AbstractHandlerMethodMapping , RequestMappingInfoHandlerMapping 和 RequestMappingHandlerMapping ,这三个类依次继承。

AbstractHandlerMethodMapping 继承自 AbstractHandlerMapping ,hook方法是 getHandlerInternal() ,下面从 初始化和应用两个方面来看一下 AbstractHandlerMethodMapping 。

AbstractHandlerMethodMapping的初始化

在 AbstractHandlerMethodMapping 中有三个Map

private final Map<T, HandlerMethod> handlerMethods
private final MultiValueMap<String, T> urlMap
private final MultiValueMap<String, HandlerMethod> nameMap

在这三个map中包含了一个泛型T,可以简单理解为查找 HandlerMethod 时的匹配条件,从子类 RequestMappingInfoHandlerMapping 的定义中就可以看出这个T的类型:

public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> {
    ....
}

对RequestMappingInfo和RequestCondition的说明放在后面。

下面看一下上面三个map的作用:

  1. urlMap:存放请求的url与匹配HandlerMethod的RequestCondition之间的映射
  2. nameMap:存放Controleller的BeanName和RequestCondition之间的映射
  3. handlerMethods:存放RequestCondition和HandlerMethod之间的映射

这三个map中 urlMap 和 nameMap 的类型是 MultiValueMap ,可以把多个value映射到一个key上,这个类型的数据结构从类定义中就可以很清楚的看出来:

public interface MultiValueMap<K, V> extends Map<K, List<V>> {
 ...
}

这三个map是 AbstractHandlerMethodMapping 最主要的容器,它的初始化主要就是对这个三个map中的内容进行初始化。

由于 AbstractHandlerMethodMapping 实现了 InitializingBean 接口,所以在启动springmvc时会自动调用 afterPropertiesSet() 方法,初始化工作就在这个方法中完成的。

@Override
public void afterPropertiesSet() {
    initHandlerMethods();
}
protected void initHandlerMethods() {
  //从spring容器中获取所有bean的beanname
        String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
                BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
                getApplicationContext().getBeanNamesForType(Object.class));
//遍历所有BeanName
        for (String beanName : beanNames) {
            //判断Bean是否包含Controller或者RequestMapping注解
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) &&
                    isHandler(getApplicationContext().getType(beanName))){
                    //将Bean初始化到上面的map
                detectHandlerMethods(beanName);
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }

在 initHandlerMethods() 中主要做的是筛选出符合可以作为HandlerMethod条件的Bean,然后调用 detectHandlerMethods() 将这些bean初始化到map中。

对于 handlerMethodsInitialized() 方法,只是一个模板方法,且子类并没有使用,所以这个方法也就没有意义。

总结一下:

  • 在AbstractHandlerMethodMapping中有三个map:urlMap,nameMap和handlerMethods,用来完成从url到RequestCondition,以及从RequestCondition到HandlerMethod的映射。
  • AbstractHandlerMethodMapping实现了InitializingBean接口,启动容器时,调用afterPropertiesSet()方法完成初始化,初始化从initHandlerMethods()方法开始,首先,获取所有beanname,然后在根据
  • beanname获取bean的实例,并判断是否符合作为HandlerMethod的条件,若符合则交给detectHandlerMethods()方法,完成最终的初始化。

再来看 detectHandlerMethods() 方法:

protected void detectHandlerMethods(final Object handler) {
//获取handler类型,此处的HandlerType仍然是类的class对象
        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);
//从clazz中找到符合条件的Method
        Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
            @Override
            public boolean matches(Method method) {
                T mapping = getMappingForMethod(method, userType);
                if (mapping != null) {
                        //将符合条件的method和RequestMappingInfo添加到mappings, 在后面使用
                    mappings.put(method, mapping);
                    return true;
                }
                else {
                    return false;
                }
            }
        });
//遍历所有符合条件的Method,并利用handler/method/requestCondition进行注册。
        for (Method method : methods) {
            registerHandlerMethod(handler, method, mappings.get(method));
        }
    }

从代码中可以看出, detectHandlerMethods() 方法主要的逻辑就是找出符合条件的Method,然后将其根据handler和requestcondtion进行注册。

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
//根据handler和Method创建HandlerMethod
        HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
//根据requestcondition从容器中获取之前的HandlerMethod
        HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);
        //如果原有的HandlerMethod和新的HandlerMethod不相等,则抛异常
        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);
//将匹配的url与RequestCondition初始化到urlMap中
        Set<String> patterns = getMappingPathPatterns(mapping);
        for (String pattern : patterns) {
            if (!getPathMatcher().isPattern(pattern)) {
                this.urlMap.add(pattern, mapping);
            }
        }
//初始化nameMap
        if (this.namingStrategy != null) {
            String name = this.namingStrategy.getName(newHandlerMethod, mapping);
            updateNameMap(name, newHandlerMethod);
        }
    }

到此, AbstractHandlerMethodMapping 的初始化就完成了。

AbstractHandlerMethodMapping的应用

入口方法依然是 getHandlerInternal()

@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//获取访问路径
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        //根据访问路径获取HandlerMethod
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }

getHandlerInternal() 中最重要的一步就是 lookupHandlerMethod()

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<Match> matches = new ArrayList<Match>();
        //根据url从urlMap中获取RequestCondition
        List<T> directPathMatches = this.urlMap.get(lookupPath);
        if (directPathMatches != null) {
        //如果获取到,添加到matches
            addMatchingMappings(directPathMatches, matches, request);
        }
        if (matches.isEmpty()) {
            // No choice but to go through all mappings...
//如果不能直接获取到,则把所有能匹配的RequestCondition天添加到matches中            addMatchingMappings(this.handlerMethods.keySet(), matches, request);
        }
        if (!matches.isEmpty()) {
        //对matches进行排序,然后获取第一个作为bestMatch返回
            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 {
        //如果仍然没有,则返回null
            return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
        }
    }

总结一下:

AbstractHandlerMethodMapping的应用:

1.获取请求路径

2.根据请求路径找到HandlerMethod

分别从handlerMethods和urlMap中查找

到此这篇关于深度理解SpringMVC中的HandlerMapping的文章就介绍到这了,更多相关SpringMVC的HandlerMapping内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文