Spring MVC的执行流程

59 阅读3分钟

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.xmlServletContainerInitializer 注册。
  • 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 → 直接写 JSON
    • void → 自己用 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. 客户端

  • DispatcherServletHttpServletResponse 刷回 Tomcat → 浏览器 → F12 看到 200/JSON。

🎯 补充问题

  1. Spring MVC 与 Struts2 最大区别?
    答:入口不同(Servlet vs Filter);参数绑定/拦截器粒度更细;与 Spring 容器无缝集成;Struts2 基于类级别,Spring MVC 基于方法级别,粒度更细,性能更高。

  2. 为什么 Spring Boot 可以省略 web.xml?
    答:Servlet3.0 规范支持 ServletContainerInitializer,Spring 提供 SpringServletContainerInitializer,在 jar 的 META-INF/services 里注册,启动时由容器 SPI 加载,动态把 DispatcherServlet 注册进去。

  3. 如何自定义参数解析器?
    答:实现 HandlerMethodArgumentResolver → 注册到 RequestMappingHandlerAdapter.setArgumentResolvers();或者继承 WebMvcConfigurer 重写 addArgumentResolvers()

  4. 如何全局包装返回值/异常?
    答:

    • 返回值:实现 ResponseBodyAdvice + @ControllerAdvice
    • 异常:实现 @ExceptionHandler + @ControllerAdvice,或者 HandlerExceptionResolver
  5. 404 到底在哪一步抛的?
    答:DispatcherServlet.getHandler() 阶段找不到对应的 HandlerMapping,就抛出 NoHandlerFoundException,默认交给 DefaultHandlerExceptionResolver 返回 404。

  6. 拦截器和过滤器的生命周期

    • HTTP Request

      • → Filter1

      • → Filter2

      • DispatcherServlet(Spring MVC 入口)

        • → HandlerMapping(解析出 HandlerMethod)
        • → HandlerInterceptor.preHandle()
        • → Controller Method(实际业务逻辑)
        • → HandlerInterceptor.postHandle()
      • → Filter2(响应返回时再次经过)

      • → Filter1

    • HTTP Response


🛠️ 源码级断点指南(本地调试)

  1. DispatcherServlet.doDispatch() —— 一切故事的起点。
  2. AbstractHandlerMapping.getHandler() —— 看映射表怎么命中。
  3. RequestMappingHandlerAdapter.invokeHandlerMethod() —— 反射调 Controller。
  4. ServletInvocableHandlerMethod.invokeAndHandle() —— 参数解析 + 结果处理。
  5. AbstractMessageConverterMethodProcessor.writeWithMessageConverters() —— JSON 写出。

📌 小结

  1. DispatcherServlet 统一入口
  2. HandlerMapping 找映射
  3. HandlerAdapter 选司机
  4. 参数解析 → 调 Controller → 返回值处理
  5. 拦截器三处钩子
  6. 视图解析 or JSON 直出
  7. 渲染/写出/完事