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);
}
步骤总结
简单来说,分四步
-
筛选所有的 bean,根据 bean 的类上有
@RequestMapping
注解或者@Controller
注解来确定当前类就是一个控制器类 -
检测控制器类中的控制器方法,方式是看这个方法上有没有
@RequestMapping
注解,同时如果控制器类上也有@RequestMapping
注解,那么控制器方法的映射信息也要包含控制器类上的@RequestMapping
注解的信息 -
进行最终的注册,将控制器方法封装成
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()));
}
}
}