RequestMappingHandlerMapping 源码解析

RequestMappingHandlerMapping 源码解析

在《SpringMVC- 第十篇:基于注解配置 SpringMVC.md》的 pathPrefixes 字段 小节中,对 RequestMappingHandlerMapping 的加载过程以及有了初步的分析,现在我们来看个完整的。

使用 Java 配置 SpringMVC

RequestMappingHandlerMapping 用于映射 @RequestMapping 表示的控制器方法这种类型的 handler。

类继承图,重点是其父类的父类 AbstractHandlerMethodMapping

注册

注册 RequestMappingHandlerMapping 类型的 bean

基于 XML

这种方式我们就不多分析了

基于 Java 注解

WebMvcConfigurationSupport 中声明了 一个 RequestMappingHandlerMapping 类型的 bean ,bean 的名字(方法名)是 requestMappingHandlerMapping

注意,RequestMappingHandlerMappingorder 属性为 0,也就是说,当所有的 HandlerMapping 根据 order 进行排序的时候,RequestMappingHandlerMapping 排第一。

@Bean
@SuppressWarnings("deprecation")
public RequestMappingHandlerMapping requestMappingHandlerMapping( @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager, @Qualifier("mvcConversionService") FormattingConversionService conversionService, @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {

    // createRequestMappingHandlerMapping 的内容其实就是  new RequestMappingHandlerMapping() ,
    // 用一个方法包一层的目的是为了方便你继承RequestMappingHandlerMapping,将来你想继承 RequestMappingHandlerMapping 的时候,重写 createRequestMappingHandlerMapping 即可,返回值类型是兼容的
    RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
    // 在所有的 HandlerMapping 中排第一
    mapping.setOrder(0);
    // 以下都是各种配置,终点不在这里,终点在配置好 RequestMappingHandlerMapping 实例之后的afterPropertiesSet(自定义初始化)方法中的工作
    mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
    mapping.setContentNegotiationManager(contentNegotiationManager);
    mapping.setCorsConfigurations(getCorsConfigurations());

    PathMatchConfigurer pathConfig = getPathMatchConfigurer();
    if (pathConfig.getPatternParser() != null) {
        mapping.setPatternParser(pathConfig.getPatternParser());
    }
    else {
        mapping.setUrlPathHelper(pathConfig.getUrlPathHelperOrDefault());
        mapping.setPathMatcher(pathConfig.getPathMatcherOrDefault());

        Boolean useSuffixPatternMatch = pathConfig.isUseSuffixPatternMatch();
        if (useSuffixPatternMatch != null) {
            mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
        }
        Boolean useRegisteredSuffixPatternMatch = pathConfig.isUseRegisteredSuffixPatternMatch();
        if (useRegisteredSuffixPatternMatch != null) {
            mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
        }
    }
    Boolean useTrailingSlashMatch = pathConfig.isUseTrailingSlashMatch();
    if (useTrailingSlashMatch != null) {
        mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
    }
    if (pathConfig.getPathPrefixes() != null) {
        mapping.setPathPrefixes(pathConfig.getPathPrefixes());
    }

    return mapping;
}

初始化 - 重点

开始看 RequestMappingHandlerMapping#afterPropertiesSet 方法,因为实现了 InitializingBean 接口,所以 Bean 初始化结束后,执行自定义初始化方法 afterPropertiesSet

public void afterPropertiesSet() {

    // 主要是填充 RequestMappingHandlerMapping#config 属性
    // RequestMappingInfo.BuilderConfiguration 类是一个存放 请求映射的配置选项的容器。创建 RequestMappingInfo 实例需要这个配置,通常跨多个 RequestMappingInfo 实例使用。即多个实例公用一个 BuilderConfiguration 配置
    // 可以看到,主要就是把在 WebMvcConfigurationSupport#requestMappingHandlerMapping 中填充的属性,拿来设置到  RequestMappingHandlerMapping#config 中
    this.config = new RequestMappingInfo.BuilderConfiguration();
    this.config.setTrailingSlashMatch(useTrailingSlashMatch());
    this.config.setContentNegotiationManager(getContentNegotiationManager());

    if (getPatternParser() != null) {
        this.config.setPatternParser(getPatternParser());
        Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch,
                "Suffix pattern matching not supported with PathPatternParser.");
    }
    else {
        this.config.setSuffixPatternMatch(useSuffixPatternMatch());
        this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
        this.config.setPathMatcher(getPathMatcher());
    }

    // 调用父类的自定义初始化方法,主要是 AbstractHandlerMethodMapping#afterPropertiesSet 
    super.afterPropertiesSet();
}

现在来看 AbstractHandlerMethodMapping#afterPropertiesSet

@Override
public void afterPropertiesSet() {
    // 初始化控制器方法
    initHandlerMethods();
}

委托给 AbstractHandlerMethodMapping#initHandlerMethods

protected void initHandlerMethods() {
    // 注意, getCandidateBeanNames 获取的是容器内所有bean的名字
    for (String beanName : getCandidateBeanNames()) {
        if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
            // 过滤掉 bean的名字带 scopedTarget. 前缀的bean
            // 开始处理
            processCandidateBean(beanName);
        }
    }
    // getHandlerMethods() :从mappingRegistry中拿到所有注册的映射信息和handler
    // handlerMethodsInitialized 在获取到所有handler后调用,在AbstractHandlerMethodMapping中的实现是空的,因此相当于一个钩子,可供子类重写,
    handlerMethodsInitialized(getHandlerMethods());
}

这里我们注意到一个 AbstractHandlerMethodMapping 的子类可以使用的钩子方法 handlerMethodsInitialized

继续看 AbstractHandlerMethodMapping#processCandidateBean

protected void processCandidateBean(String beanName) {
    Class<?> beanType = null;
    try {
        // 根据bean的名字获取bean的类型
        beanType = obtainApplicationContext().getType(beanName);
    }
    catch (Throwable ex) {
        // An unresolvable bean type, probably from a lazy bean - let's ignore it.
        if (logger.isTraceEnabled()) {
            logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
        }
    }
    // 1. isHandler (由RequestMappingHandlerMapping实现)根据类上有 @RequestMapping 注解或者 @Controller 注解来确定当前类就是一个处理器类,
    // 注意,只是一个处理器类,还没到具体的处理器方法
    if (beanType != null && isHandler(beanType)) {
        // 检测控制器类中的控制器方法
        detectHandlerMethods(beanName);
    }
}

接着看 AbstractHandlerMethodMapping#detectHandlerMethods,核心方法,检测指定的控制器类中的所有的控制器方法

protected void detectHandlerMethods(Object handler) {
    Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass());

    if (handlerType != null) {
        // 如果此类型是经过CGLIB代理的代理类型,则获取用户创建的原始的被代理类下
        Class<?> userType = ClassUtils.getUserClass(handlerType);
        // 2. 根据对目标类的方法的元数据(比如注解啊)的查找,返回目标类型上的特定的方法。
        // 经过 对 getMappingForMethod 方法的研究可知,这个对元数据的查找,找的实际上就是 @RequestMapping 注解,有 @RequestMapping 注解的方法才会返回,同时返回 保存着 @RequestMapping 的元数据和其他跟映射相关的配置信息的类 RequestMappingInfo 
        // 因此,T 的类型为 RequestMappingInfo
        Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                // method -> {
                //     try {
                //         return getMappingForMethod(method, userType);
                //     }
                //     catch (Throwable ex) {
                //         throw new IllegalStateException("Invalid mapping on handler class [" +
                //                 userType.getName() + "]: " + method, ex);
                //     }
                // }
                // 是一个lambda表达式,强制转化为 MethodIntrospector.MetadataLookup 的接口实现,作为参数,传递给 MethodIntrospector的selectMethods方法
                // 同时,getMappingForMethod方法返回的,也就是inspect方法返回的,是 RequestMappingInfo 类型,即 此函数中的泛型类型 T 为 RequestMappingInfo
                (MethodIntrospector.MetadataLookup<T>) method -> {
                    try {
                        // getMappingForMethod的具体实现在RequestMappingHandlerMapping中
                        return getMappingForMethod(method, userType);
                    }
                    catch (Throwable ex) {
                        throw new IllegalStateException("Invalid mapping on handler class [" +
                                userType.getName() + "]: " + method, ex);
                    }
                });
        if (logger.isTraceEnabled()) {
            logger.trace(formatMappings(userType, methods));
        }
        methods.forEach((method, mapping) -> {
            Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
            // 3. 进行最终的注册
            registerHandlerMethod(handler, invocableMethod, mapping);
        });
    }
}

先简单分析一下 MethodIntrospector#selectMethods 方法,这个方法的作用是,根据对目标类的方法的元数据(比如注解啊)的查找,返回目标类型上的特定的方法。核心逻辑是,metadataLookup.inspect(specificMethod); 返回不为 null,specificMethod 就是我们想要的目标方法,即控制器方法。

public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
    final Map<Method, T> methodMap = new LinkedHashMap<>();
    Set<Class<?>> handlerTypes = new LinkedHashSet<>();
    Class<?> specificHandlerType = null;

    if (!Proxy.isProxyClass(targetType)) {
        specificHandlerType = ClassUtils.getUserClass(targetType);
        handlerTypes.add(specificHandlerType);
    }
    handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));

    for (Class<?> currentHandlerType : handlerTypes) {
        final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);

        // 只对满足 ReflectionUtils.USER_DECLARED_METHODS 条件(是否是用户声明的方法)的方法执行回调函数   
        // 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);
        //         }
        //     }
        // }
        // 这个回调函数的作用是,调用 metadataLookup ,如果返回的不为 null,就添加到 methodMap 中,
        // 也就是说,只要metadataLookup不为null,就是我们想要的目标方法,即控制器方法
        ReflectionUtils.doWithMethods(currentHandlerType, 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;
}

我们传入的 metadataLookup 的是一个 lambda 表达式,核心逻辑是调用 getMappingForMethod 方法方法。

getMappingForMethod 方法的功能是返回指定的类中的指定的方法上的特定的信息,信息的具体 Java 类型不定。AbstractHandlerMethodMapping 中没有实现,具体实现,由 AbstractHandlerMethodMapping 的各个子类实现,这里我们看 RequestMappingHandlerMapping 的实现,RequestMappingHandlerMapping#getMappingForMethod,返回的信息的类型是 RequestMappingInfo过滤的条件(即 metadataLookup 返回不为 null 的条件)是传入的 method 上必须有 @RequestMapping 注解

@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    // 获取 方法上的 @RequestMapping 注解的信息,加上前面 RequestMappingHandlerMapping#config 属性,也就是 WebMvcConfigurationSupport#requestMappingHandlerMapping 中填充的内容,构造成了 RequestMappingInfo 这个实例
    // 如果没有 @RequestMapping  则返回null
    RequestMappingInfo info = createRequestMappingInfo(method);
    if (info != null) {
        //  获取 类上的 @RequestMapping 注解的信息,加上前面 RequestMappingHandlerMapping#config 属性,
        // 如果没有 @RequestMapping  则返回null
        RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
        if (typeInfo != null) {
            // 如果类上有  @RequestMapping 注解的信息, 则进行组合
            // 这就是为什么控制器方法的实际映射路径为所在类的 @RequestMapping 注解的映射路径 + 方法本身的 @RequestMapping 注解的映射路径 
            info = typeInfo.combine(info);
        }
        // 查看有没有配置前缀 参考 《SpringMVC-第十篇:基于注解配置SpringMVC.md》的`pathPrefixes 字段`小节
        String prefix = getPathPrefix(handlerType);
        if (prefix != null) {
            // 如果有,在 info 的最前面添加上
            info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
        }
    }
    return info;
}

至此,指定类中的控制器方法多扫描完毕,开始进行下一步操作,AbstractHandlerMethodMapping#registerHandlerMethod

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
    // 注解使用 mappingRegistry 注册,所以 所有的控制器方法的信息,实际上都是保存在 mappingRegistry 中,这里就不深挖了
    this.mappingRegistry.register(mapping, handler, method);
}

其中, AbstractHandlerMethodMapping.MappingRegistry#register

public void register(T mapping, Object handler, Method method) {
    this.readWriteLock.writeLock().lock();
    try {
        // 将控制器和方法封装成一个 HandlerMethod 实例,这个实例也就是最终的处理器(handler)实例
        HandlerMethod handlerMethod = createHandlerMethod(handler, method);
        validateMethodMapping(handlerMethod, mapping);

        // 获取 RequestMappingInfo 中的映射路径,可以有多个,添加到 MappingRegistry.pathLookup 中
        Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
        for (String path : directPaths) {
            this.pathLookup.add(path, mapping);
        }

        String name = null;
        if (getNamingStrategy() != null) {
            name = getNamingStrategy().getName(handlerMethod, mapping);
            addMappingName(name, handlerMethod);
        }

        CorsConfiguration config = initCorsConfiguration(handler, method, mapping);
        if (config != null) {
            config.validateAllowCredentials();
            this.corsLookup.put(handlerMethod, config);
        }

        this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directPaths, name));
    }
    finally {
        this.readWriteLock.writeLock().unlock();
    }
}

这里,我们需要注意一点就是,就是将控制器方法,封装成了 HandlerMethod,作为最终的 handler 实例,

protected HandlerMethod createHandlerMethod(Object handler, Method method) {
    if (handler instanceof String) {
        return new HandlerMethod((String) handler, obtainApplicationContext().getAutowireCapableBeanFactory(), method);
    }
    return new HandlerMethod(handler, method);
}

所有 @Controller 注解修饰的类的 @RequestMapping 注解修饰的方法,都会非封装成 HandlerMethod 类型的 handler 实例,方便后续的区分和调用,

将来,我们还会看到别的类型的 handler 实例,具体请看《SpringMVC 控制器方法(handler)适配器 - HandlerAdapter.md》的 适配器的意义 小节

我们来简单看一下 HandlerMethod 的作用:HandlerMethod 是一个类,其作用是封装由方法和 bean 构成的 handler 的信息。提供对方法参数、方法返回值、方法注解等信息的统一的方便访问。HandlerMethod 类实例可以使用 bean 实例或 bean 名称创建 (例如 lazy-init bean, prototype bean)。

继承树:

HandlerMethod 的子类 InvocableHandlerMethodServletInvocableHandlerMethod 将在控制器方法参数解析和返回值映射中起到很大的作用,

具体请看《SpringMVC-RequestMappingHandlerAdapter 源码解析.md》中 HandlerMethod 小节

至此,我们知道,所有的控制器方法(handler)的信息都注册到了 AbstractHandlerMethodMapping#MappingRegistry 中,其中映射路径,都注册到了 AbstractHandlerMethodMapping.MappingRegistry#pathLookup 中。

RequestMappingHandlerMapping 继承了 AbstractHandlerMethodMapping,同时最终的 bean 的类型也是 RequestMappingHandlerMapping

因此我们应该这么说,所有的控制器方法(handler)的信息都注册到了 RequestMappingHandlerMapping#MappingRegistry 中,其中映射路径,都注册到了 RequestMappingHandlerMapping.MappingRegistry#pathLookup 中。

此外,RequestMappingHandlerMapping 还对 AbstractHandlerMethodMapping#registerHandlerMethod 进行了重写,RequestMappingHandlerMapping#registerHandlerMethod,除了调用父类的 registerHandlerMethod 方法,其实也没干啥。

@Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
    // super.registerHandlerMethod(handler, method, mapping);
    // 如果控制器方法的参数有@RequestBody注解修饰的参数,则设置 ConsumesRequestCondition 的 condition.setBodyRequired(annot.getBoolean("required"));
    // 我猜意思是,如果控制器方法有@RequestBody注解修饰的参数,那么请求就必须有请求体,大概是这个意思
    updateConsumesCondition(mapping, method);
}

步骤总结

简单来说,分四步

  1. 筛选所有的 bean,根据 bean 的类上有 @RequestMapping 注解或者 @Controller 注解来确定当前类就是一个控制器类

  2. 检测控制器类中的控制器方法,方式是看这个方法上有没有 @RequestMapping 注解,同时如果控制器类上也有 @RequestMapping 注解,那么控制器方法的映射信息也要包含控制器类上的 @RequestMapping 注解的信息

  3. 进行最终的注册,将控制器方法封装成 HandlerMethod 类型的实例,作为 handler 实例,注册到 RequestMappingHandlerMapping#MappingRegistry 中。

使用

RequestMappingHandlerMapping 初始化好之后,在 DispatcherServletonRefresh 方法中调用 initStrategies,其中也包含了,initHandlerMappings(context);,这个时候,获取所有的 HandlerMapping,已经是都已经初始化好了的 HandlerMapping

也就是说 HandlerMapping 实例的初始化(各种映射路径和 handler 的映射关系的扫描与保存),是在 DispatcherServlet 启动之前,就已经做好了的。

路径匹配

《SpringMVC- 第十篇:基于注解配置 SpringMVC.md》的 AbstractHandlerMethodMapping 小节中已经了解过了

DispatcherServlet#doDispatch 中的 mappedHandler = getHandler(processedRequest); 最终调用的是 HandlerMapping#getHandler,而 HandlerMapping#getHandler 的抽象实现类 AbstractHandlerMapping 在实现此方法的时候,将其委托给 AbstractHandlerMapping#getHandlerInternalAbstractHandlerMapping#getHandlerInternal 是抽象方法,需要子类重写。其中两个字类 AbstractHandlerMethodMappingAbstractUrlHandlerMapping 都对其进行了重写。

这里我们只看 AbstractHandlerMethodMapping 的重写

AbstractHandlerMethodMapping#getHandlerInternal

@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    // 初始化查找路径
    String lookupPath = initLookupPath(request);
    // 映射注册锁,我猜意思是在查找的时候不允许注册,
    this.mappingRegistry.acquireReadLock();
    try {
        // 查找控制器方法
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }
    finally {
        // 释放锁
        this.mappingRegistry.releaseReadLock();
    }
}

接下来查看 AbstractHandlerMapping#initLookupPath,注意,这个方法放到了 AbstractHandlerMapping 中,因为在 AbstractUrlHandlerMapping#getHandlerInternal 实现中也使用了这个方法,SpringMVC 的代码真的很整洁,能提取到父类中的一定提取到父类。

protected String initLookupPath(HttpServletRequest request) {
    // 当前 AbstractHandlerMapping 的 patternParser 为空,表示没有配置 patternParser ,返回false
    // 不为空,表示配置了 patternParser 即要使用 patternParser 返回true
    if (usesPathPatterns()) { 
        // 如果决定要使用 patternParser 
        request.removeAttribute(UrlPathHelper.PATH_ATTRIBUTE);
        RequestPath requestPath = ServletRequestPathUtils.getParsedRequestPath(request);
        String lookupPath = requestPath.pathWithinApplication().value();
        // 很奇妙,通过requestPath.pathWithinApplication获取了查找路径之后,还通过UrlPathHelper.removeSemicolonContent去掉一下分号
        return UrlPathHelper.defaultInstance.removeSemicolonContent(lookupPath);
    }
    else {
        // resolveAndCacheLookupPath 实际上就是 getLookupPathForRequest 方法包了一层,
        // 同时将查出来的数据放到请求域属性中,属性名为 org.springframework.web.util.UrlPathHelper.path
        return getUrlPathHelper().resolveAndCacheLookupPath(request);
    }
}

然后开始看 AbstractHandlerMethodMapping#lookupHandlerMethod 方法,会根据是否配置了 patternParser,调用不同的匹配方法。具体我们就不去深究了

@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<>();
    // 还记得前面我们初始化 RequestMappingInfoHandlerMapping 的时候,把所有的 控制器方法的映射信息都放到了 RequestMappingInfoHandlerMapping的mappingRegistry 中
    // mappingRegistry 根据 lookupPath 直接匹配,不考虑通配符啥的,也不考虑请求方法是否匹配,只看是否有控制器方法的映射路径跟请求路径直接完全的相同
    // 当实现类是RequestMappingInfoHandlerMapping 的时候,T 就是 RequestMappingInfo
    List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
    if (directPathMatches != null) {
        // 如果配置了 patternParser , 最终会调用 PathPattern的matches方法
        addMatchingMappings(directPathMatches, matches, request);
    }
    if (matches.isEmpty()) {
        // 如果没有直接匹配的,那就把 mappingRegistry 中注册的所有的 RequestMappingInfo 都拿来进行匹配尝试
        addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
    }
    // 匹配上了,进行筛选,哪个最匹配的
    if (!matches.isEmpty()) {
        // 如果只有一个,默认第一个是最佳匹配
        Match bestMatch = matches.get(0);
        // 如果 matches 有多个,排序后的第一个才是最佳匹配
        if (matches.size() > 1) {
            Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
            // 排序
            matches.sort(comparator);
            bestMatch = matches.get(0);
            if (logger.isTraceEnabled()) {
                logger.trace(matches.size() + " matching mappings: " + matches);
            }
            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();
                String uri = request.getRequestURI();
                throw new IllegalStateException(
                        "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
            }
        }
        request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
        // 如果没有配置 patternParser,会在这里, 最终会调用 AntPathMatcher的doMatch方法
        handleMatch(bestMatch.mapping, lookupPath, request);
        return bestMatch.handlerMethod;
    }
    else {
        return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
    }
}

其中看看 AbstractHandlerMethodMapping#addMatchingMappings

private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
    for (T mapping : mappings) {
        // 对 mappings 中的 T (当实现类是RequestMappingInfoHandlerMapping 的时候,T就是RequestMappingInfo)进行检查
        // 检查给定的RequestMappingInfo是否与当前请求匹配,并返回一个具有与当前请求匹配条件的(可能是新的)实例
        // 如果 与当前请求不匹配,就会返回null
        // 这个方法在也AbstractHandlerMethodMapping中是抽象方法,由子类 RequestMappingInfoHandlerMapping#getMatchingMapping 实现 
        T match = getMatchingMapping(mapping, request);
        if (match != null) {
            matches.add(new Match(match,
                    this.mappingRegistry.getRegistrations().get(mapping).getHandlerMethod()));
        }
    }
}