AbstractHandlerMethodMapping实现接口getHandlerInternal,定义查找流程

RequestMappingInfoHandlerMapping根据RequestMappingInfo,细化匹配条件,并在匹配不到情况下,顽强的使用RequestCondition一再尝试匹配

虽然 RequestMappingHandlerMapping是受益方,但在这边什么都没做(就是在初始化时,根据@Controller,@RequestMapping注解生成RequestMappingInfo;并根据这两个注解判断是否目标Handler  实现isHandler)

AbstractHandlerMethodMapping实现接口getHandlerInternal

  1. 使用UrlPathHelper查找request对应的path

  2. 查找path对应的HandlerMethod

    2.1 从urlMap中直接等值匹配查找匹配条件RequestMappingInfo

    2.2 如果等值查找到匹配条件,将其添加到match条件中

    2.3 如果没有找到匹配条件,使用所有的handlerMethod的RequestMappingInfo进行匹配

    2.4 对匹配到的Match进行排序,取出最高优先级的Match,并核对是否是唯一的最高优先级

    2.5 对匹配到条件,没有匹配到条件的两种情况,分别进行封装

  3. 封装HandlerMethod,确保bean中存的是实例

// AbstractHandlerMethodMapping

实现接口getHandlerInternal

 package org.springframework.web.servlet.handler
// AbstractHandlerMethodMapping<T>
/**
* Look up a handler method for the given request.
*/
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 就是request对应的url
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 查找到处理器,这边的处理器会封装成HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
// 确保bean中存的是实例
return (handlerMethod != null) ? handlerMethod.createWithResolvedBean() : null;
}

// AbstractHandlerMethodMapping

package org.springframework.web.servlet.handler;
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean  {
/**
* Look up the best-matching handler method for the current request.
* If multiple matches are found, the best match is selected.
* @param lookupPath mapping lookup path within the current servlet mapping
* @param request the current request
* @return the best-matching handler method, or {@code null} if no match
* @see #handleMatch(Object, String, HttpServletRequest)
* @see #handleNoMatch(Set, String, HttpServletRequest)
*/
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
// 从urlMap中直接等值匹配查找匹配条件RequestMappingInfo
List<T> directPathMatches = this.urlMap.get(lookupPath);
if (directPathMatches != null) {
//
addMatchingMappings(directPathMatches, matches, request);
} if (matches.isEmpty()) {
// No choice but to go through all mappings
// 没有匹配的情况下,遍历handlerMethods的全部匹配条件进行查找
addMatchingMappings(this.handlerMethods.keySet(), matches, request);
} if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator); 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();
// 不能有相同的最优Match
throw new IllegalStateException(
"Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" +
m1 + ", " + m2 + "}");
}
}
// 就是往request域中缓存url中解析出来的参数,mediaType等,这边RequestMappingHandlerMapping也覆写了一下
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
// RequestMappingHandlerMapping
return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
}
}
}

// AbstractHandlerMethodMapping

查找具体符合条件的RequestCondition

 package org.springframework.web.servlet.handler;
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean { private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
for (T mapping : mappings) {
T match = getMatchingMapping(mapping, request);
if (match != null) {
matches.add(new Match(match, handlerMethods.get(mapping)));
}
}
}

// AbstractHandlerMethodMapping

     /**
* Check if a mapping matches the current request and return a (potentially
* new) mapping with conditions relevant to the current request.
* @param mapping the mapping to get a match for
* @param request the current HTTP servlet request
* @return the match, or {@code null} if the mapping doesn't match
*/
protected abstract T getMatchingMapping(T mapping, HttpServletRequest request);

我们来看看RequestMappingInfoHandlerMapping中的实现,从RequestMappingInfo中查找符合的RequestCondition

// RequestMappingInfoHandlerMapping

     /**
* Check if the given RequestMappingInfo matches the current request and
* return a (potentially new) instance with conditions that match the
* current request -- for example with a subset of URL patterns.
* @return an info in case of a match; or {@code null} otherwise.
*/
@Override
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
return info.getMatchingCondition(request);
}

// AbstractHandlerMethodMapping

     /**
* Invoked when a matching mapping is found.
* @param mapping the matching mapping
* @param lookupPath mapping lookup path within the current servlet mapping
* @param request the current request
*/
protected void handleMatch(T mapping, String lookupPath, HttpServletRequest request) {
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath);
}

RequestMappingInfoHandlerMapping中又对其进行了覆写,具体是干啥用的,等看了HandlerAdaptor再说吧

     /**
* Expose URI template variables, matrix variables, and producible media types in the request.
* @see HandlerMapping#URI_TEMPLATE_VARIABLES_ATTRIBUTE
* @see HandlerMapping#MATRIX_VARIABLES_ATTRIBUTE
* @see HandlerMapping#PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE
*/
@Override
protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
super.handleMatch(info, lookupPath, request); Set<String> patterns = info.getPatternsCondition().getPatterns();
String bestPattern = patterns.isEmpty() ? lookupPath : patterns.iterator().next();
request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern); Map<String, String> uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);
Map<String, String> decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables);
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables); if (isMatrixVariableContentAvailable()) {
request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, extractMatrixVariables(request, uriVariables));
} if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
}
}
     /**
* Invoked when no matching mapping is not found.
* @param mappings all registered mappings
* @param lookupPath mapping lookup path within the current servlet mapping
* @param request the current request
* @throws ServletException in case of errors
*/
protected HandlerMethod handleNoMatch(Set<T> mappings, String lookupPath, HttpServletRequest request)
throws Exception { return null;
}

RequestMappingInfoHandlerMapping,覆写,不死心,再匹配一次

// RequestMappingInfoHandlerMapping

     /**
* Iterate all RequestMappingInfos once again, look if any match by URL at
* least and raise exceptions accordingly.
* @throws HttpRequestMethodNotSupportedException if there are matches by URL
* but not by HTTP method
* @throws HttpMediaTypeNotAcceptableException if there are matches by URL
* but not by consumable/producible media types
*/
@Override
protected HandlerMethod handleNoMatch(Set<RequestMappingInfo> requestMappingInfos,
String lookupPath, HttpServletRequest request) throws ServletException { Set<String> allowedMethods = new LinkedHashSet<String>(4); Set<RequestMappingInfo> patternMatches = new HashSet<RequestMappingInfo>();
Set<RequestMappingInfo> patternAndMethodMatches = new HashSet<RequestMappingInfo>(); for (RequestMappingInfo info : requestMappingInfos) {
if (info.getPatternsCondition().getMatchingCondition(request) != null) {
patternMatches.add(info);
if (info.getMethodsCondition().getMatchingCondition(request) != null) {
patternAndMethodMatches.add(info);
}
else {
for (RequestMethod method : info.getMethodsCondition().getMethods()) {
allowedMethods.add(method.name());
}
}
}
} if (patternMatches.isEmpty()) {
return null;
}
else if (patternAndMethodMatches.isEmpty() && !allowedMethods.isEmpty()) {
throw new HttpRequestMethodNotSupportedException(request.getMethod(), allowedMethods);
} Set<MediaType> consumableMediaTypes;
Set<MediaType> producibleMediaTypes;
Set<String> paramConditions; if (patternAndMethodMatches.isEmpty()) {
consumableMediaTypes = getConsumableMediaTypes(request, patternMatches);
producibleMediaTypes = getProducibleMediaTypes(request, patternMatches);
paramConditions = getRequestParams(request, patternMatches);
}
else {
consumableMediaTypes = getConsumableMediaTypes(request, patternAndMethodMatches);
producibleMediaTypes = getProducibleMediaTypes(request, patternAndMethodMatches);
paramConditions = getRequestParams(request, patternAndMethodMatches);
} if (!consumableMediaTypes.isEmpty()) {
MediaType contentType = null;
if (StringUtils.hasLength(request.getContentType())) {
try {
contentType = MediaType.parseMediaType(request.getContentType());
}
catch (IllegalArgumentException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
}
}
throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<MediaType>(consumableMediaTypes));
}
else if (!producibleMediaTypes.isEmpty()) {
throw new HttpMediaTypeNotAcceptableException(new ArrayList<MediaType>(producibleMediaTypes));
}
else if (!CollectionUtils.isEmpty(paramConditions)) {
String[] params = paramConditions.toArray(new String[paramConditions.size()]);
throw new UnsatisfiedServletRequestParameterException(params, request.getParameterMap());
}
else {
return null;
}
}

SpringMVC源码解读 - HandlerMapping - RequestMappingHandlerMapping请求分发的更多相关文章

  1. SpringMVC源码解读 - HandlerMapping - RequestMappingHandlerMapping初始化

    RequestMappingHandlerMapping ,用于注解@Controller,@RequestMapping来定义controller. @Controller @RequestMapp ...

  2. SpringMVC源码解读 - HandlerMapping

    SpringMVC在请求到handler处理器的分发这步是通过HandlerMapping模块解决的.handlerMapping 还处理拦截器. 先看看HandlerMapping的继承树吧 可以大 ...

  3. SpringMVC源码解读 - HandlerMapping - AbstractUrlHandlerMapping系列request分发

    AbstractHandlerMapping实现HandlerMapping接口定的getHandler 1. 提供getHandlerInternal模板方法给子类实现 2. 如果没有获取Handl ...

  4. SpringMVC源码解读 - HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化

    AbstractDetectingUrlHandlerMapping是通过扫描方式注册Handler,收到请求时由AbstractUrlHandlerMapping的getHandlerInterna ...

  5. SpringMVC源码解读 - HandlerMapping - SimpleUrlHandlerMapping初始化

    摘要: SimpleUrlHandlerMapping只是参与Handler的注册,请求映射时由AbstractUrlHandlerMapping搞定. 初始化时,通过setMappings(Prop ...

  6. SpringMVC源码解读 - RequestMapping注解实现解读 - RequestCondition体系

    一般我们开发时,使用最多的还是@RequestMapping注解方式. @RequestMapping(value = "/", param = "role=guest& ...

  7. SpringMVC源码解读 - RequestMapping注解实现解读

    SpringMVC源码解读 - RequestMapping注解实现解读 - RequestCondition体系  https://www.cnblogs.com/leftthen/p/520840 ...

  8. SpringMVC源码解读 - RequestMapping注解实现解读 - RequestMappingInfo

    使用@RequestMapping注解时,配置的信息最后都设置到了RequestMappingInfo中. RequestMappingInfo封装了PatternsRequestCondition, ...

  9. springMVC源码分析--HandlerMapping(一)

    HandlerMapping的工作就是为每个请求找到合适的请求找到一个处理器handler,其实现机制简单来说就是维持了一个url到Controller关系的Map结构,其提供的实际功能也是根据req ...

随机推荐

  1. HashMap的实现原理,以及在JDK1.7和1.8的区别

    1.JDK1.7 HashMap是Java中大家最常用的一个map实现类,其为键值对也就是key-value的形式.他的数据结构则是采用的位桶和链表相结合的形式完成了,即拉链法.具体如下图所示: Ha ...

  2. ApacheTraffic Server 使用ssd 以及裸盘

    使用裸设备后可以使用ATS自身的文件子系统,可以获得更好的IO性能,也是官方推荐的方式.下面为例 删除分区,不使用操作系统自带分区 `fdisk -l /dev/sde` 修改相关设备权限并创新相关设 ...

  3. 星级评分原理 N次重写的分析

    使用的是雪碧图,用的软件是CSS Sprite Tools 第一次实现与分析: <!DOCTYPE html> <html> <head> <meta cha ...

  4. 微信小程序--getLocation需要在app.json中声明permission字段

    在微信小程序开发中,需要获取用户所在地理位置,结果提示‘getLocation需要在app.json中声明permission字段’ 这是因为开发者需要填写获取用户地理位置的用途说明. 具体解决方法: ...

  5. WordConut

    一.代码地址:https://gitee.com/cainiaoY/WordCount 二.代码: import java.io.*; import java.util.regex.*; public ...

  6. 云笔记项目-笔记列表弹出"分享移动删除"子菜单

    业务需求: 笔记列表里还有一个按钮可以弹出子菜单,要求做到以下几点: (1)点击选中的笔记行的弹出按钮后,弹出子菜单,再次点击,子菜单收回. (2)选中其他笔记后,子菜单消失.效果如下图所示: 业务分 ...

  7. yum方面的知识

    修改CentOS默认yum源为国内yum镜像源 1.mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bac ...

  8. 【VBA】セールの値は配列に変換方法

    方法一 Sub test1() //変数の定義 Dim a() As Integer, iRow As Long, i As Integer //非空白のセールまでの行を取得 iRow = Cells ...

  9. Django缓存设置

    由于Django构建得是动态网站,每次客户端请求都要严重依赖数据库,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcache中, ...

  10. 牛客小白月赛12 J 月月查华华的手机(序列自动机)

    ---恢复内容开始--- 题目来源:https://ac.nowcoder.com/acm/contest/392/J 题意: 题目描述 月月和华华一起去吃饭了.期间华华有事出去了一会儿,没有带手机. ...