RequestMappingHandlerMapping 源码解析

RequestMappingHandlerMapping 源码解析

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

使用 Java 配置 SpringMVC

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

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

注册

注册 RequestMappingHandlerMapping 类型的 bean

基于 XML

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

基于 Java 注解

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

注意,RequestMappingHandlerMapping 的 order 属性为 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 的子类 InvocableHandlerMethod、ServletInvocableHandlerMethod 将在控制器方法参数解析和返回值映射中起到很大的作用,

具体请看《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 初始化好之后,在 DispatcherServlet 的 onRefresh 方法中调用 initStrategies,其中也包含了,initHandlerMappings(context);,这个时候,获取所有的 HandlerMapping,已经是都已经初始化好了的 HandlerMapping 了

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

路径匹配

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

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

这里我们只看 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()));
        }
    }
}