Spring MVC 请求全链路源码解析:基于 DispatcherServlet 的责任链执行模型
Spring MVC 作为 Spring 生态中处理 Web 请求的核心模块,其设计精妙地融合了“前端控制器”与“责任链”模式,将 HTTP 请求的处理过程拆解为高度标准化、可插拔的执行链路。理解这一链路,不仅能精准定位 404、500、响应格式异常等“诡异问题”,更是掌握 Spring Web 开发底层原理的关键一步。
本文将从 源码实现角度,完整拆解一个 HTTP 请求从进入容器到返回响应的 8 个核心阶段,深入剖析 DispatcherServlet 如何协调各组件完成请求分发,并揭示常见陷阱背后的真正原因。
一、核心组件与整体架构
在深入链路前,先明确 Spring MVC 的六大核心组件及其职责:
| 组件 | 职责 | 典型实现 |
|---|---|---|
DispatcherServlet |
前端控制器,所有请求的统一入口 | org.springframework.web.servlet.DispatcherServlet |
HandlerMapping |
将请求 URI 映射到具体的 Controller 方法 | RequestMappingHandlerMapping |
HandlerAdapter |
适配并执行目标 Handler(如带 @RequestMapping 的方法) |
RequestMappingHandlerAdapter |
HandlerInterceptor |
拦截请求生命周期,实现前置/后置/收尾逻辑 | 自定义实现 HandlerInterceptor 接口 |
ViewResolver |
将逻辑视图名解析为具体视图对象(如 JSP) | InternalResourceViewResolver |
HttpMessageConverter |
序列化/反序列化请求体与响应体(如 JSON/XML) | MappingJackson2HttpMessageConverter |
注意:自 Spring 5.3 起,
HandlerInterceptorAdapter已被废弃,推荐直接实现HandlerInterceptor接口。
这些组件通过 DispatcherServlet 有机串联,形成一条清晰的责任链:
请求 → 路由 → 拦截 → 执行 → 拦截 → 渲染 → 收尾
二、请求执行全链路(8 阶段源码级拆解)
以下以一个标准 GET 请求为例,完整还原其在 Spring MVC 中的生命周期。
阶段 1:请求入口 —— DispatcherServlet.doDispatch()
所有请求首先进入 DispatcherServlet 的 doDispatch() 方法,这是整个处理流程的总控中心:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
// ... 初始化变量
try {
processedRequest = checkMultipart(request); // 处理文件上传
mappedHandler = getHandler(processedRequest); // 阶段2:路由匹配
if (mappedHandler == null) {
noHandlerFound(processedRequest, response); // 返回404
return;
}
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 执行拦截器 preHandle(阶段3)
if (!mappedHandler.applyPreHandle(processedRequest, response)) return;
// 执行 Controller 方法(阶段4)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv); // 阶段5
} catch (Exception ex) {
dispatchException = ex;
}
// 处理结果:渲染视图 / 异常处理(阶段6)
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} finally {
// 清理资源 + 异步收尾(阶段8)
if (multipartRequestParsed) cleanupMultipart(processedRequest);
}
此方法是理解整个链路的“钥匙”。
阶段 2:路由匹配 —— HandlerMapping.getHandler()
getHandler() 遍历所有 HandlerMapping(默认优先级:RequestMappingHandlerMapping 最高),尝试匹配请求:
public final HandlerExecutionChain getHandler(HttpServletRequest request) {
Object handler = getHandlerInternal(request); // 核心匹配逻辑
if (handler == null) return null;
// 构建包含拦截器的执行链
HandlerExecutionChain chain = getHandlerExecutionChain(handler, request);
// 处理 CORS(跨域预检)
if (CorsUtils.isPreFlightRequest(request)) {
return null; // 预检请求不继续后续流程
}
return chain;
}
RequestMappingHandlerMapping.getHandlerInternal() 会:
- 去除上下文路径(如
/app/api/user→/api/user) - 匹配
@RequestMapping的 path、method、headers、produces等条件 - 封装为
HandlerMethod(含类、方法、参数、注解等元数据)
若无匹配项,
getHandler()返回null,触发noHandlerFound()→ HTTP 404
阶段 3:拦截器前置 —— preHandle()
HandlerExecutionChain.applyPreHandle() 正序执行所有拦截器的 preHandle():
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) {
for (int i = 0; i < interceptors.size(); i++) {
if (!interceptors.get(i).preHandle(request, response, handler)) {
triggerAfterCompletion(request, response, null); // 提前收尾
return false;
}
interceptorIndex = i; // 记录已成功执行的拦截器索引
}
return true;
}
- 返回
true:继续链路 - 返回
false:立即终止,仅执行已成功拦截器的afterCompletion()
典型用途:权限校验、日志埋点、防重放攻击
阶段 4:方法执行 —— HandlerAdapter.handle()
RequestMappingHandlerAdapter 是处理 @RequestMapping 方法的核心适配器:
protected ModelAndView handleInternal(...) {
checkRequest(request); // 校验 HTTP 方法、Session 等
mav = invokeHandlerMethod(request, response, handlerMethod); // 核心
prepareResponse(response); // 设置缓存头等
return mav;
}
invokeHandlerMethod() 内部完成三大任务:
-
参数解析
通过HandlerMethodArgumentResolver解析:@RequestParam→RequestParamMethodArgumentResolver@RequestBody→RequestResponseBodyMethodProcessor@PathVariable→PathVariableMethodArgumentResolver
-
方法调用
利用反射执行目标 Controller 方法 -
返回值处理
通过HandlerMethodReturnValueHandler分支处理:- 若方法/类标注
@ResponseBody→ 使用HttpMessageConverter序列化为 JSON,直接写入 response 输出流 - 否则 → 封装为
ModelAndView,等待视图渲染
- 若方法/类标注
此阶段抛出异常,将进入
HandlerExceptionResolver异常处理链
阶段 5:拦截器后置 —— postHandle()
Controller 成功执行后,逆序调用 postHandle():
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) {
for (int i = interceptors.size() - 1; i >= 0; i--) {
interceptors.get(i).postHandle(request, response, handler, mv);
}
}
- 可修改
ModelAndView(如添加全局字段) - 异常或异步场景下不会执行
阶段 6:响应处理 —— processDispatchResult()
这是决定最终响应形态的关键分叉点:
private void processDispatchResult(..., ModelAndView mv, Exception ex) {
if (ex != null) {
mv = processHandlerException(request, response, handler, ex); // 异常处理
}
if (mv != null && !mv.wasCleared()) {
render(mv, request, response); // 视图渲染
}
triggerAfterCompletion(...); // 阶段8
}
两种响应路径:
| 路径 | 触发条件 | 处理方式 |
|---|---|---|
| REST API(JSON) | 方法/类有 @ResponseBody |
HttpMessageConverter 直接写入响应体,mv == null 或无视图名 |
| 页面渲染(HTML) | 返回 String / ModelAndView |
ViewResolver 解析视图 → forward 到 JSP/Thymeleaf 模板 |
经典坑点:浏览器默认
Accept: text/html,若接口未指定produces = "application/json",可能误走视图渲染路径 → 404(因无对应页面)
阶段 7:内容协商 —— ContentNegotiationManager
内容协商决定“返回什么格式”,其优先级为:
@RequestMapping(produces = "...") > Accept Header > 默认配置
ProducesRequestCondition.match() 会校验客户端 Accept 是否兼容接口声明的 produces。
解决方案:
- 接口级:
@GetMapping(value = "/user", produces = MediaType.APPLICATION_JSON_VALUE) - 全局级(推荐):
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { configurer.defaultContentType(MediaType.APPLICATION_JSON) .ignoreAcceptHeader(false); } }
阶段 8:拦截器收尾 —— afterCompletion()
无论成功或失败,最终都会逆序执行 afterCompletion():
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) {
for (int i = interceptorIndex; i >= 0; i--) {
try {
interceptors.get(i).afterCompletion(request, response, handler, ex);
} catch (Throwable t) {
logger.error("afterCompletion threw exception", t);
}
}
}
- 用于资源清理(如关闭连接、释放锁)
- 参数
ex可判断是否发生异常
执行顺序总结:
preHandle:正序(先注册先执行)postHandle/afterCompletion:逆序(先注册后执行)→ 符合“栈”式责任链
三、关键扩展点与实战问题解析
1. 为什么浏览器访问 REST 接口返回 404?
- 根本原因:内容协商选择
text/html→ 触发视图解析 → 无对应页面 → 404 - 解决方案:强制
produces = "application/json"或配置全局默认 Content-Type
2. 拦截器执行顺序为何“前正后逆”?
- 设计哲学:类似“函数调用栈”——先进入的拦截器,最后退出
- 保证资源释放顺序与申请顺序相反(如 A 获取锁 → B 获取锁 → B 释放 → A 释放)
3. 异常处理链优先级
Spring 默认按以下顺序尝试处理异常:
ExceptionHandlerExceptionResolver(@ExceptionHandler)ResponseStatusExceptionResolver(@ResponseStatus)DefaultHandlerExceptionResolver(处理 400/405/415 等标准错误)
可通过 @Order 或自定义 HandlerExceptionResolver 插入更高优先级处理器。
四、执行链路全景图

五、结语:从“会用”到“精通”的跨越
Spring MVC 的请求处理链路,是一套高度内聚、低耦合、可扩展的经典设计范式。它不仅是一个 Web 框架,更是一本“如何构建可维护系统”的教科书。
掌握这条链路,意味着你能:
- 精准定位问题:快速判断异常发生在路由、参数绑定、还是响应渲染阶段;
- 灵活定制行为:通过拦截器、消息转换器、异常处理器等扩展点,打造专属 Web 能力;
- 规避隐性陷阱:不再被“内容协商”“视图解析”等非业务逻辑困扰。
真正的框架高手,不是记住 API,而是理解请求的“一生”。
当你能在脑海中完整复现doDispatch()的每一步,Spring MVC 便不再是“黑盒”,而成为你手中的利器。
💡 建议实践:在 IDE 中打断点跟踪
DispatcherServlet.doDispatch(),配合本文阅读,效果翻倍。
浙公网安备 33010602011771号