spring源码解析-web系列(三):九大组件之HandlerMapping

spring源码解析-web系列(一):启动
spring源码解析-web系列(二):处理请求的过程
spring源码解析-web系列(三):九大组件之HandlerMapping
spring源码解析-web系列(四):九大组件之HandlerAdapter
spring源码解析-web系列(五):解析请求参数
spring源码解析-web系列(六):九大组件之ViewResolver
spring源码解析-web系列(七):九大组件之HandlerExceptionResolver

转载请标明出处:
https://blog.csdn.net/bingospunky/article/details/97795113
本文出自马彬彬的博客

HandlerMapping

HandlerMapping的作用就是通过HttpServletRequest查找Handler,Handler后面可以用来处理逻辑的单元,比如我们配置的一个 @RequestMapping 注解的方法,一个***org.springframework.web.servlet.mvc.Controller***类(该接口已经被废弃了,推荐使用注解)。

HandlerMapping接口就定义了一个方法:

HandlerExecutionChain getHandler(HttpServletRequest var1) throws Exception

该方法返回HandlerExecutionChain对象,HandlerExecutionChain是对Handler的封装,它包含Handler和HandlerInterceptor,对HandlerInterceptor的调用是通过HandlerExecutionChain来调用的。

HandlerMapping的继承关系如下:

图1:

https://qingtian-blog.oss-cn-beijing.aliyuncs.com/spring%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-web%E7%B3%BB%E5%88%97%E4%B8%89-%E4%B9%9D%E5%A4%A7%E7%BB%84%E4%BB%B6%E4%B9%8BHandlerMapping_1.png

从继承关系来看,HandlerMapping分为两大类:左边是基于Method的注解来匹配;右边是url来匹配。

DispatcherServlet加载时,默认就加载两个HandlerMapping,一个是RequestMappingHandlerMapping,另一个是BeanNameUrlHandlerMapping。

AbstractHandlerMapping

HandlerMapping的整体结构在AbstractHandlerMapping中实现,根据request查找Handler和Interceptors,组合成HandlerExecutionChain返回。

查找Handler的过程通过***getHandlerInternal***让子类去实现,查找Interceptors的过程在如下代码中。

代码0 (org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandlerExecutionChain):

    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
        HandlerExecutionChain chain = handler instanceof HandlerExecutionChain ? (HandlerExecutionChain)handler : new HandlerExecutionChain(handler);
        chain.addInterceptors(this.getAdaptedInterceptors());
        String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
        Iterator var5 = this.mappedInterceptors.iterator();

        while(var5.hasNext()) {
            MappedInterceptor mappedInterceptor = (MappedInterceptor)var5.next();
            if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                chain.addInterceptor(mappedInterceptor.getInterceptor());
            }
        }

        return chain;
    }

AbstractUrlHandlerMapping

代码1 (org.springframework.web.servlet.handler.AbstractUrlHandlerMapping.getHandlerInternal):

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

其中代码1第4行lookupHandler方法和第22行buildPathExposingHandler方法比较关键。

代码1第4行代码通过url路径和HttpServletRequest查找Handler,在AbstractUrlHandlerMapping中有一个***private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();***,url和Handler在初始化的时候存放在这个handlerMap中。

代码2 (org.springframework.web.servlet.handler.AbstractUrlHandlerMapping.lookupHandler):

    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 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);
            if (handler == null) {
                Assert.isTrue(bestPatternMatch.endsWith("/"));
                handler = this.handlerMap.get(bestPatternMatch.substring(0, bestPatternMatch.length() - 1));
            }
            // 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;
    }

代码2第3行直接根据url获取Handler,如果获取到就直接返回。有可能获取不到,比如url带参数或者有通配符,这时通过代码2第14第24行代码进行模式匹配,可能匹配到若干个url。然后通过代码2第26第33行代码把获取到url进行排序,取出第一个url便是最合适的url。排序的过程如下:

代码3 (org.springframework.util.AntPathMatcher.AntPatternComparator.compare(String pattern1, String pattern2)):

    @Override
    public int compare(String pattern1, String pattern2) {
    PatternInfo info1 = new PatternInfo(pattern1);
    PatternInfo info2 = new PatternInfo(pattern2);

    if (info1.isLeastSpecific() && info2.isLeastSpecific()) {
        return 0;
    }
    else if (info1.isLeastSpecific()) {
        return 1;
    }
    else if (info2.isLeastSpecific()) {
        return -1;
    }

    boolean pattern1EqualsPath = pattern1.equals(path);
    boolean pattern2EqualsPath = pattern2.equals(path);
    if (pattern1EqualsPath && pattern2EqualsPath) {
        return 0;
    }
    else if (pattern1EqualsPath) {
        return -1;
    }
    else if (pattern2EqualsPath) {
        return 1;
    }

    if (info1.isPrefixPattern() && info2.getDoubleWildcards() == 0) {
        return 1;
    }
    else if (info2.isPrefixPattern() && info1.getDoubleWildcards() == 0) {
        return -1;
    }

    if (info1.getTotalCount() != info2.getTotalCount()) {
        return info1.getTotalCount() - info2.getTotalCount();
    }

    if (info1.getLength() != info2.getLength()) {
        return info2.getLength() - info1.getLength();
    }

    if (info1.getSingleWildcards() < info2.getSingleWildcards()) {
        return -1;
    }
    else if (info2.getSingleWildcards() < info1.getSingleWildcards()) {
        return 1;
    }

    if (info1.getUriVars() < info2.getUriVars()) {
        return -1;
    }
    else if (info2.getUriVars() < info1.getUriVars()) {
        return 1;
    }

    return 0;
    }

代码4 (org.springframework.util.AntPathMatcher.AntPatternComparator.PatternInfo相关代码):

    public PatternInfo(String pattern) {
        this.pattern = pattern;
        if (this.pattern != null) {
            initCounters();
            this.catchAllPattern = this.pattern.equals("/**");
            this.prefixPattern = !this.catchAllPattern && this.pattern.endsWith("/**");
        }
        if (this.uriVars == 0) {
            this.length = (this.pattern != null ? this.pattern.length() : 0);
        }
    }

    protected void initCounters() {
        int pos = 0;
        while (pos < this.pattern.length()) {
            if (this.pattern.charAt(pos) == '{') {
                this.uriVars++;
                pos++;
            }
            else if (this.pattern.charAt(pos) == '*') {
                if (pos + 1 < this.pattern.length() && this.pattern.charAt(pos + 1) == '*') {
                    this.doubleWildcards++;
                    pos += 2;
                }
                else if (pos > 0 && !this.pattern.substring(pos - 1).equals(".*")) {
                    this.singleWildcards++;
                    pos++;
                }
                else {
                    pos++;
                }
            }
            else {
                pos++;
            }
        }
    }

通过代码3和代码4可以发现,在对url进行比较的时候,先判这个url是不是形如*** /** 的全匹配,再对url进行计算有多少个匹配参数、单星号通配符、双星号通配符。最后根据这些数据得出结论,比如形如 /** ***的全匹配优先级最低、字符串完全匹配优先级最高、不包含变量时,长度越长约优先、相同长度时变量越少越优先等。

代码2第50~57行从url里提取参数。代码2第61行buildPathExposingHandler方法把Handler构造成HandlerExecutionChain,并且添加PathExposingHandlerInterceptor和UriTemplateVariablesHandlerInterceptor两个内部拦截器。

AbstractUrlHandlerMapping的子类

SimpleUrlHandlerMapping: 直接把url和Handler对象存放在map里,在初始化的时候调用registerHandler方法注册。

AbstractDetectingUrlHandlerMapping: 抽象类,从BeanFactory获取所有的Bean,获取该Bean对应的url,如果url存在,则注册url和Bean对象。通过Bean获取url的过程有子类具体实现。

AbstractControllerUrlHandlerMapping: 抽象类,也是AbstractDetectingUrlHandlerMapping的子类,通过一个Bean是否是***org.springframework.web.servlet.mvc.Controller***的子类或者是否被@Controller注解来判断一个Bean是否可以作为Handler。它有两个子类,区别是构造url的方式不同,一个使用beanName构造,一个使用ClassName构造。

AbstractHandlerMethodMapping

AbstractHandlerMethodMapping类中有三个Map:

代码5 (org.springframework.web.servlet.handler.AbstractHandlerMethodMapping):

    private final Map<T, HandlerMethod> handlerMethods = new LinkedHashMap();
    private final MultiValueMap<String, T> urlMap = new LinkedMultiValueMap();
    private final MultiValueMap<String, HandlerMethod> nameMap = new LinkedMultiValueMap();

handlerMethods: 存放匹配条件(RequestCondition,也就是@RequestMapping注解对应的信息)和HandlerMethod的匹配关系。

urlMap: 存放url和RequestCondition的匹配关系,这里Map的类型为MultiValueMap,一个url可能会对应多个@RequestMapping。

nameMap: 暂不考虑。

AbstractHandlerMethodMapping初始化过程

AbstractHandlerMethodMapping的初始化是在initHandlerMethods方法进行的。

代码6 (org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.initHandlerMethods):

    protected void initHandlerMethods() {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Looking for request mappings in application context: " + this.getApplicationContext());
        }

        String[] beanNames = this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.getApplicationContext(), Object.class) : this.getApplicationContext().getBeanNamesForType(Object.class);
        String[] var2 = beanNames;
        int var3 = beanNames.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            String beanName = var2[var4];
            if (!beanName.startsWith("scopedTarget.") && this.isHandler(this.getApplicationContext().getType(beanName))) {
                this.detectHandlerMethods(beanName);
            }
        }

        this.handlerMethodsInitialized(this.getHandlerMethods());
    }

代码6第6行获取到BeanFactory里的所有对象,如果该对象包含@Controller注解或@RequestMapping注解,那么调用代码6第16行代码探测该对象里的方法。

代码7 (org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.detectHandlerMethods):

    protected void detectHandlerMethods(Object handler) {
        Class<?> handlerType = handler instanceof String ? this.getApplicationContext().getType((String)handler) : handler.getClass();
        final Map<Method, T> mappings = new IdentityHashMap();
        final Class<?> userType = ClassUtils.getUserClass(handlerType);
        Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
            public boolean matches(Method method) {
                T mapping = AbstractHandlerMethodMapping.this.getMappingForMethod(method, userType);
                if (mapping != null) {
                    mappings.put(method, mapping);
                    return true;
                } else {
                    return false;
                }
            }
        });
        Iterator var6 = methods.iterator();

        while(var6.hasNext()) {
            Method method = (Method)var6.next();
            this.registerHandlerMethod(handler, method, mappings.get(method));
        }

    }

代码7传地进来的参数是对象或者对象的beanName,这里把该对象叫做Handler,同时也把它里面的方法叫做Handler,为了区分他们,分别叫他们为对象Handler和Handler。

代码7第3行创建的mappings存放Method和RequestCondition的对应关系。遍历该对象Handler里的所有方法,通过代码7第7行的getMappingForMethod方法获取RequestCondition,获取的逻辑就是判断该方法是否被@RequestMapping注解,如果被注解,通过该注解和类上的注解一起构造RequestCondition对象。对于获取到RequestCondition的方法,把该方法和RequestCondition放进mappings里。

代码7第20行代码,遍历mappings进行注册。

代码8 (org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.registerHandlerMethod):

    protected void registerHandlerMethod(Object handler, Method method, T mapping) {
        HandlerMethod newHandlerMethod = this.createHandlerMethod(handler, method);
        HandlerMethod oldHandlerMethod = (HandlerMethod)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.");
        } else {
            this.handlerMethods.put(mapping, newHandlerMethod);
            if (this.logger.isInfoEnabled()) {
                this.logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);
            }

            Set<String> patterns = this.getMappingPathPatterns(mapping);
            Iterator var7 = patterns.iterator();

            while(var7.hasNext()) {
                String pattern = (String)var7.next();
                if (!this.getPathMatcher().isPattern(pattern)) {
                    this.urlMap.add(pattern, mapping);
                }
            }

            if (this.namingStrategy != null) {
                String name = this.namingStrategy.getName(newHandlerMethod, mapping);
                this.updateNameMap(name, newHandlerMethod);
            }

        }
    }

代码8主要的内容就是把找到的Handler注册到HandlerMapping里。
代码8第2行通过对象(或beanName)和Method构造HandlerMethod,代码8第7行把RequestCondition(mapping对象)和Handler(newHandlerMethod对象)存放在handlerMethods里。代码8第12~20行代码通过RequestCondition(mapping对象)获取url,然后把url和RequestCondition(mapping对象)的关系存放在urlMap里。

至此,AbstractHandlerMethodMapping初始化过程就结束了。

AbstractHandlerMethodMapping执行过程

AbstractHandlerMethodMapping作为AbstractHandlerMapping的子类,在执行过程中,最重要的方法就是getHandlerInternal。

代码9 (org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal):

    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        String lookupPath = this.getUrlPathHelper().getLookupPathForRequest(request);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Looking up handler method for path " + lookupPath);
        }

        HandlerMethod handlerMethod = this.lookupHandlerMethod(lookupPath, request);
        if (this.logger.isDebugEnabled()) {
            if (handlerMethod != null) {
                this.logger.debug("Returning handler method [" + handlerMethod + "]");
            } else {
                this.logger.debug("Did not find handler method for [" + lookupPath + "]");
            }
        }

        return handlerMethod != null ? handlerMethod.createWithResolvedBean() : null;
    }

代码9第7行代码,通过url和HttpServletRequest获取HandlerMethod。

代码10 (org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod):

    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<AbstractHandlerMethodMapping<T>.Match> matches = new ArrayList();
        List<T> directPathMatches = (List)this.urlMap.get(lookupPath);
        if (directPathMatches != null) {
            this.addMatchingMappings(directPathMatches, matches, request);
        }

        if (matches.isEmpty()) {
            this.addMatchingMappings(this.handlerMethods.keySet(), matches, request);
        }

        if (!matches.isEmpty()) {
            Comparator<AbstractHandlerMethodMapping<T>.Match> comparator = new AbstractHandlerMethodMapping.MatchComparator(this.getMappingComparator(request));
            Collections.sort(matches, comparator);
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
            }

            AbstractHandlerMethodMapping<T>.Match bestMatch = (AbstractHandlerMethodMapping.Match)matches.get(0);
            if (matches.size() > 1) {
                AbstractHandlerMethodMapping<T>.Match secondBestMatch = (AbstractHandlerMethodMapping.Match)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 + "}");
                }
            }

            this.handleMatch(bestMatch.mapping, lookupPath, request);
            return bestMatch.handlerMethod;
        } else {
            return this.handleNoMatch(this.handlerMethods.keySet(), lookupPath, request);
        }
    }

代码10第三行通过url获取RequestCondition,代码10第5行使用这些RequestCondition对HttpServletRequest进行匹配,如果匹配成功,把他们添加到matches中。代码10第13行~27行在匹配的matches中选择一个最合适的HandlerMethod。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值