Spring MVC 02 - HandlerMapping

HandlerMapping

// HandlerMapping
+HandlerExecutionChain getHandler(HttpServletRequest request)

根据 Request 匹配handler 返回 HandlerExecutionChain

// HandlerExecutionChain
private final Object handler;
private HandlerInterceptor[] interceptors;

HandlerExecutionChain 包含一个实际的handler 和一串interceptors

实际常用的实现是 RequestMappingHandlerMapping。讲Controller 的 @RequestMapping 修饰的方法作为 handler

image.png

AbstractHandlerMapping
HandlerMapping 的入口,管理 handler interceptors. 和一些列工具类 UrlPathHelper, AntPathMatcher 等。
AbstractHandlerMethodMapping<T>
注册handler 和匹配规则,实现根据 request 搜索匹配 handler 的逻辑。T 代表匹配规则。
RequestMappingInfoHandlerMapping
extends AbstractHandlerMethodMapping<RequestMappingInfo> 将匹配规则实现具体到 RequestMappingInfo 类。
同时向请求中添加一些关于解析结果的属性
RequestMappingHandlerMapping

初始化

AbstractHandlerMapping

  1. initApplicationContext()
    detectMappedInterceptors 找到所有mapped interceptor, 添加入adaptedInterceptors list中

AbstractHandlerMethodMapping<T>

afterPropertiesSet() 中调用 initHandlerMethods()

  1. 遍历所有bean, 使用模版方法 isHandler 判断一个bean 是否为handler。对于handler 调用 detectHandlerMethods

  2. detectHandlerMethods() 遍历handler bean 下所有的方法,使用模版方法 T getMappingForMethod(Method method, Class<?> handlerType) 判断并返回当前method 对应的匹配条件。使用registerHandler 注册下来。

  3. registerHandlerMethod(Object handler, Method method, T mapping) 调用 MappingRegistry.register() 注册handler 及其mapping.

RequestMappingHandlerMapping

isHandler
实现模板方法,检查bean 是否为 Controller 或者 RequestMapping

getMappingForMethod
实现模板方法

  1. 依次对 handler 和 method 调用 createRequestMappingInfo
    2 createRequestMappingInfo 获取 RequestMapping 的 annotation, 调用 createRequestMappingInfo 生成 RequestMappingInfo
    3 createRequestMappingInfo 从 RequestMapping 从取出 path, method, header... 加上 config,生成RequestMappingInfo

MappingRegistry

// 存储匹配条件 - handlerMethod 的 mapping
-Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, HandlerMethod>();
// 不带匹配规则的简单url - handler methods 的 mapping。方便在查找简单url时快速找到对应的 handler methods
-final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<String, T>();

register(T mapping, Object handler, Method method)

  1. 调用 createHandlerMethod(handler, method) 创建 handlerMethod
  2. 检查该mapping 是否已经注册过
  3. 加入 mappingLookup
  4. getDirectUrls 尝试从该mapping 中获取简单url,如有简单url,则加入 urlLookup

匹配Handler

//HandlerMapping
HandlerExecutionChain getHandler(HttpServletRequest request)

AbstractHandlerMapping

getHandler()

  1. 调用模版方法 ·Object getHandlerInternal(HttpServletRequest)` 获取 handler
  2. 调用 getHandlerExecutionChain. 将handler和所有adaptedInterceptors 添加入 HandlerExectionChain

AbstractHandlerMethodMapping

  1. getUrlPathHelper().getLookupPathForRequest(request) 提取请求路径

  2. 调用 HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) 匹配handler
    2.1 从urlMap 中尝试直接取出所有满足条件的匹配
    2.2 否则则取出所有注册的匹配条件
    2.3 模版方法getMatchingMapping 获取准确的mapping 并过滤掉完全不匹配的条件
    2.4 用MatchComparator 对所有匹配条件进行排序,找到最佳的。排序算法由模版方法 Comparator<T> getMappingComparator(HttpServletRequest request) 提供
    2.4 调用模板方法 handleMatch

RequestMappingInfoHandlerMapping

实现模板方法
getMappingComparator()

protected Comparator<RequestMappingInfo> getMappingComparator(final HttpServletRequest request) {
    return (info1, info2) -> info1.compareTo(info2, request);
}

handleMatch()
添加匹配结果相关属性到 request

handleNoMatch()
针对没匹配上的情况抛出相应异常

RequestMappingInfo

image.png
  • RequestCondition<T> 中的T 是这个匹配条件可以combine/compare 的对象,实际的实现里面都是自己。比如RequestMappingInfo implements RequestCondition<RequestMappingInfo>
  • getMatchingCondition 返回null (不匹配)或者一个新的 Condition (可以去掉无关的信息)
  • compareTo 在执行了 getMatchingCondition 的前提下比较(保证去除了无关信息)

RequestMappingInfo
整合了所有请求相关的 Condition

private final PatternsRequestCondition patternsCondition;
private final RequestMethodsRequestCondition methodsCondition;
private final ParamsRequestCondition paramsCondition;
private final HeadersRequestCondition headersCondition;
private final ConsumesRequestCondition consumesCondition;
private final ProducesRequestCondition producesCondition;
private final RequestConditionHolder customConditionHolder;

匹配request 的时候会逐个Condition 检查,都匹配上则通过。
compare 的时候也会逐个Condition 进行比较

总结大致流程

image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容

  • 今天是P后第8天,P后综合征还在延续…… 昨晚没有做到早睡,早上起来精神状态不佳。早睡对我比较有挑战性,每天都提醒...
    周正萍阅读 163评论 0 0
  • 【日精进打卡第1天】 【知~学习】 《六项精进》1遍 共2遍 《大学》1遍 共2遍 【经典名句分享】 因为你要做一...
    寇寇寇阅读 166评论 0 0
  • 穿过春梦的蝴蝶 惊蛰和清明 桃色染透了清风 透明的玻璃橱窗 折射着彩虹的光 落在信封里的彩蝶 隐藏着没有说出的话 ...
    听雪1014阅读 322评论 0 0
  • 肉包放寒假了,肉爸见肉包整天在家里搞破坏就拉着肉包说“肉包啊,今天,爸爸给你讲一下我国四大名著之一《西游记》”滔滔...
    南笙北爱阅读 155评论 0 0