Spring MVC 的整个执行流程,从浏览器请求到服务端,到返回数据到浏览器。
🌐 总览图
浏览器 ──► DispatcherServlet ──► HandlerMapping ──► HandlerAdapter
▲ │ │
│ ▼ ▼
│ 拦截器 preHandle() 执行 Controller
│ │ │
│ ▼ ▼
│ 拦截器 postHandle() 返回 ModelAndView
│ │ │
│ ▼ ▼
│ ViewResolver 渲染视图(JSON/HTML)
│ │ │
└──────────────────────────────┴─────────────────────┘
🔍 详细 10 步拆解
1. 览器发请求
GET /order/1024?token=abc
→ 到达 Tomcat,被封装成 HttpServletRequest/Response。
2. 入 DispatcherServlet(前端控制器)
- 本质是一个标准的
HttpServlet,在web.xml或ServletContainerInitializer注册。 - Spring Boot 场景下由
DispatcherServletAutoConfiguration帮你装好。
3. 处理Controller和URI的映射关系:HandlerMapping
- 默认植入
RequestMappingHandlerMapping(Spring 3.1+)。 - 根据
@RequestMapping、@GetMapping等注解建立 URI ⇄ HandlerMethod 的映射表。 - 返回一个
HandlerExecutionChain= 目标HandlerMethod+ 一组HandlerInterceptor。
4. 处理请求进来和返回出去的数据类型转换:HandlerAdapter
- 默认用
RequestMappingHandlerAdapter。 - 负责把请求参数、注解、返回值等“翻译”成 Java 世界看得懂的东西。
- 核心扩展点:
HttpMessageConverter(JSON 用 Jackson/Fastjson,XML 用 JAXB…)。
5. Controller方法入参解析:HandlerMethodArgumentResolver
- 把 request 中的:
- 查询串 →
@RequestParam - JSON 体 →
@RequestBody - 路径变量 →
@PathVariable - Header →
@RequestHeader - Cookie →
@CookieValue - 自己定义的对象 → 经过 Converter/Formatter 绑定
- 查询串 →
- 最终凑成调用
Controller方法的实参列表。
6. Controller 业务代码
@GetMapping("/order/{id}")
public OrderVO detail(@PathVariable Long id,
@LoginUser User user) {
return orderService.query(id, user);
}
- 这里就是你写 if/else、调 Service、撸数据库的地方。
- 返回结果可以是:
String→ 逻辑视图名ModelAndView@ResponseBody/ResponseEntity→ 直接写 JSONvoid→ 自己用HttpServletResponse写流
7. 器回调:preHandle() → Controller → postHandle() → afterCompletion()
- 可以做登录校验、日志、计时、幂等 Token 等。
- 只要
preHandle()返回false,后序流程直接短路。 - 过滤器(HandlerInterceptor)
8. 处理:HandlerMethodReturnValueHandler
- 如果是
@ResponseBody,选RequestResponseBodyMethodProcessor:- 通过
HttpMessageConverter把对象写 JSON,设置Content-Type: application/json;charset=UTF-8。
- 通过
- 如果是视图名,选
ViewNameMethodReturnValueHandler:- 交给
ViewResolver去找/WEB-INF/jsp/order.jsp还是 Thymeleaf 模板。
- 交给
9. 渲染
- 传统 JSP:把 Model 丢进
HttpServletRequest属性 → JSP 编译成 Servlet → HTML。 - 模板引擎:Thymeleaf、Freemarker、Mustache 等,拼好 HTML。
- 前后端分离:根本没有9,8就把 JSON 刷出去了。
10. 客户端
DispatcherServlet把HttpServletResponse刷回 Tomcat → 浏览器 → F12 看到 200/JSON。
🎯 补充问题
-
Spring MVC 与 Struts2 最大区别?
答:入口不同(Servlet vs Filter);参数绑定/拦截器粒度更细;与 Spring 容器无缝集成;Struts2 基于类级别,Spring MVC 基于方法级别,粒度更细,性能更高。 -
为什么 Spring Boot 可以省略 web.xml?
答:Servlet3.0规范支持ServletContainerInitializer,Spring 提供SpringServletContainerInitializer,在 jar 的META-INF/services里注册,启动时由容器 SPI 加载,动态把DispatcherServlet注册进去。 -
如何自定义参数解析器?
答:实现HandlerMethodArgumentResolver→ 注册到RequestMappingHandlerAdapter.setArgumentResolvers();或者继承WebMvcConfigurer重写addArgumentResolvers()。 -
如何全局包装返回值/异常?
答:- 返回值:实现
ResponseBodyAdvice+@ControllerAdvice。 - 异常:实现
@ExceptionHandler+@ControllerAdvice,或者HandlerExceptionResolver。
- 返回值:实现
-
404 到底在哪一步抛的?
答:DispatcherServlet.getHandler()阶段找不到对应的HandlerMapping,就抛出NoHandlerFoundException,默认交给DefaultHandlerExceptionResolver返回 404。 -
拦截器和过滤器的生命周期
-
HTTP Request
-
→ Filter1
-
→ Filter2
-
→ DispatcherServlet(Spring MVC 入口)
- → HandlerMapping(解析出 HandlerMethod)
- → HandlerInterceptor.preHandle()
- → Controller Method(实际业务逻辑)
- → HandlerInterceptor.postHandle()
-
→ Filter2(响应返回时再次经过)
-
→ Filter1
-
-
HTTP Response
-
🛠️ 源码级断点指南(本地调试)
DispatcherServlet.doDispatch()—— 一切故事的起点。AbstractHandlerMapping.getHandler()—— 看映射表怎么命中。RequestMappingHandlerAdapter.invokeHandlerMethod()—— 反射调 Controller。ServletInvocableHandlerMethod.invokeAndHandle()—— 参数解析 + 结果处理。AbstractMessageConverterMethodProcessor.writeWithMessageConverters()—— JSON 写出。
📌 小结
- DispatcherServlet 统一入口
- HandlerMapping 找映射
- HandlerAdapter 选司机
- 参数解析 → 调 Controller → 返回值处理
- 拦截器三处钩子
- 视图解析 or JSON 直出
- 渲染/写出/完事