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 有机串联,形成一条清晰的责任链:
请求 → 路由 → 拦截 → 执行 → 拦截 → 渲染 → 收尾以下以一个标准 GET 请求为例,完整还原其在 Spring MVC 中的生命周期。
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);
}此方法是理解整个链路的“钥匙”。
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
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()典型用途:权限校验、日志埋点、防重放攻击
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 → PathVariableMethodArgumentResolverHandlerMethodReturnValueHandler 分支处理:
@ResponseBody → 使用 HttpMessageConverter 序列化为 JSON,直接写入 response 输出流ModelAndView,等待视图渲染此阶段抛出异常,将进入
HandlerExceptionResolver异常处理链
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(如添加全局字段)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(因无对应页面)
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);
}
}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:逆序(先注册后执行)→ 符合“栈”式责任链text/html → 触发视图解析 → 无对应页面 → 404produces = "application/json" 或配置全局默认 Content-TypeSpring 默认按以下顺序尝试处理异常:
ExceptionHandlerExceptionResolver(@ExceptionHandler)ResponseStatusExceptionResolver(@ResponseStatus)DefaultHandlerExceptionResolver(处理 400/405/415 等标准错误)可通过 @Order 或自定义 HandlerExceptionResolver 插入更高优先级处理器。

Spring MVC 的请求处理链路,是一套高度内聚、低耦合、可扩展的经典设计范式。它不仅是一个 Web 框架,更是一本“如何构建可维护系统”的教科书。
掌握这条链路,意味着你能:
真正的框架高手,不是记住 API,而是理解请求的“一生”。 当你能在脑海中完整复现
doDispatch()的每一步,Spring MVC 便不再是“黑盒”,而成为你手中的利器。💡 建议实践:在 IDE 中打断点跟踪
DispatcherServlet.doDispatch(),配合本文阅读,效果翻倍。