📌 阅读前提示 :第 16 篇看到
doDispatch先getHandler再getHandlerAdapter。本篇拆开这两大体系的内部结构------@RequestMapping是如何变成一条条映射的?适配器又是如何反射调用你的 Controller 方法的?这是 MVC 源码「承上启下」的一篇。
一、引子:为什么需要「Mapping」和「Adapter」两个东西
初学者常混淆:HandlerMapping 找到了方法,为什么还要 HandlerAdapter 才能执行?
原因在解耦 :Spring 要支持多种 Handler 类型(@RequestMapping 方法、实现 Controller 接口的类、HttpRequestHandler、甚至函数式端点)。Mapping 只负责「把 URL 映射到某个 Handler 对象」,但「怎么调用这个 Handler」因类型而异------这部分交给 Adapter。这是典型的适配器模式。
二、HandlerMapping 体系
2.1 继承结构
HandlerMapping (接口)
├─ AbstractHandlerMapping(模板方法,含拦截器链、排序)
│ ├─ AbstractHandlerMethodMapping ← 处理「方法级」映射
│ │ └─ RequestMappingHandlerMapping ★(@RequestMapping 主力)
│ └─ AbstractUrlHandlerMapping ← 处理「类级」URL 映射
│ └─ SimpleUrlHandlerMapping / BeanNameUrlHandlerMapping
└─ RouterFunctionMapping(函数式端点)
2.2 @RequestMapping 如何被注册
在容器启动的 afterPropertiesSet 阶段(第 7 篇生命周期)RequestMappingHandlerMapping 扫描所有 Bean:
java
// RequestMappingHandlerMapping 继承自 AbstractHandlerMethodMapping
@Override
public void afterPropertiesSet() {
initHandlerMethods(); // ← 初始化时扫描所有 @RequestMapping
}
protected void initHandlerMethods() {
for (String beanName : getCandidateBeanNames()) {
if (beanType != null && isHandler(beanType)) { // isHandler: 类上有 @Controller 或 @RequestMapping
detectHandlerMethods(beanName); // 解析每个方法上的 @RequestMapping
}
}
}
// detectHandlerMethods 内部:把 Method + 映射条件 注册进 MappingRegistry
// 最终形成:RequestCondition → HandlerMethod 的查找表
💡 重点 :
@RequestMapping信息被解析成RequestMappingInfo(含 path、method、params、headers 等条件),连同HandlerMethod(方法 + Bean 引用)一起存进MappingRegistry。请求来时getHandler就是在这张表里匹配。
2.3 getHandler 匹配逻辑
java
// AbstractHandlerMethodMapping.getHandlerInternal
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 1. 按 URL 查
List<Match> matches = new ArrayList<>();
addMatchingMappings(directPathMatches, matches, request);
// 2. 按 RequestMappingInfo 的条件(method/params/headers)再过滤
if (matches.isEmpty()) { /* 找不到 → 可能抛出 Ambiguous / No mapping */ }
// 3. 多匹配时按规则排序取最优(如更具体的 path 优先)
Match bestMatch = matches.get(0);
return bestMatch.handlerMethod;
}
三、HandlerAdapter 体系
3.1 主要适配器
| 适配器 | 支持的 Handler 类型 |
|---|---|
RequestMappingHandlerAdapter |
@RequestMapping 标注的方法(最常用) |
HttpRequestHandlerAdapter |
实现 HttpRequestHandler 的类 |
SimpleControllerHandlerAdapter |
实现老式 Controller 接口的类 |
HandlerFunctionAdapter |
函数式端点 HandlerFunction |
3.2 RequestMappingHandlerAdapter 如何反射调用
java
// RequestMappingHandlerAdapter.java
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return invokeHandlerMethod(request, response, (HandlerMethod) handler); // 核心
}
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) {
// 1. 用 HandlerMethod 创建 ServletInvocableHandlerMethod
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
// 2. 注入参数解析器、返回值处理器(第18篇详解)
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
// 3. 真正反射调用
invocableMethod.invokeAndHandle(webRequest, mavContainer);
return getModelAndView(mavContainer, modelFactory, webRequest);
}
📌 结论 :
HandlerMethod里保存了目标 Bean 引用 + Method 对象;适配器用反射method.invoke(bean, args)完成调用,参数args由参数解析器 填充(第 18 篇),返回值由返回值处理器处理。
四、Mapping 与 Adapter 体系图

五、常见误区
| 误区 | 正解 |
|---|---|
| 一个 URL 只能对应一个方法 | 可以有多匹配,按 RequestMappingInfo 条件排序取最优;条件冲突才报错 |
| Adapter 负责找 Handler | 不,Mapping 找 Handler,Adapter 只负责「调用」 |
@RequestMapping 运行时才解析 |
不,容器启动 afterPropertiesSet 阶段就扫描注册进 MappingRegistry |
| HandlerMethod 持有的是 Class | 不,持有的是Bean 实例引用 + Method ,反射时直接 invoke(bean, args) |
| 所有请求都走 RequestMappingHandlerMapping | 仅注解 Controller;老式 Controller 接口走 SimpleControllerHandlerAdapter |
🧪 面试题自测
- HandlerMapping 和 HandlerAdapter 为什么是两个体系?各自职责?
@RequestMapping在什么时机被解析注册?存在哪里?RequestMappingInfo包含哪些匹配条件?- 多个方法匹配同一 URL 时 Spring 如何取舍?
RequestMappingHandlerAdapter内部反射调用的关键对象是什么?- 参数和返回值是谁处理的?(引出第 18 篇)
🔧 Debug 小技巧
在 RequestMappingHandlerMapping.afterPropertiesSet 断点,观察 MappingRegistry 里 registry 的映射条目数量随你写的 Controller 增长;再在 RequestMappingHandlerAdapter.invokeHandlerMethod 断点,看 invocableMethod 里的 bean 和 method 如何被反射调用。
下一篇预告
第 18 篇:参数解析器 HandlerMethodArgumentResolver 与返回值处理器 HandlerMethodReturnValueHandler------你的 @RequestParam、@RequestBody、@PathVariable 究竟是怎么被塞进方法参数的?返回 String / ModelAndView / @ResponseBody 又如何被处理?
如果这篇对你有帮助,欢迎 点赞 · 收藏 · 关注 三连支持。
Spring 源码系列共 30 篇,由浅入深持续更新中。有疑问或想深挖的源码点,评论区告诉我,下篇见。