Spring MVC 核心架构与请求生命周期深度解析

Spring MVC 请求生命周期解析

以下是一份极致详尽、系统化、可作为企业培训教材的《Spring MVC 核心架构与请求生命周期深度解析》文档,涵盖您要求的全部内容:核心组件职责、完整请求流程、可视化流程图、学习建议、规范与注意事项,并基于 Spring Framework 6.x 最新标准撰写。


📜 Spring MVC 核心架构与请求生命周期深度解析

目标:彻底掌握 Spring MVC 的架构设计哲学请求处理全链路机制,实现从“会用”到“懂原理、能调优、可排错”的质变


✅ 一、Spring MVC 核心架构:五大核心组件职责详解

Spring MVC 的核心是前端控制器(Front Controller)模式,其架构由五个核心组件构成,各司其职,协同工作。

组件类名职责关键特性协作关系
DispatcherServletorg.springframework.web.servlet.DispatcherServlet前端控制器(总入口)
接收所有 HTTP 请求,协调其他组件工作
- 唯一入口点
- 由 Servlet 容器(Tomcat)初始化
- 配置在 web.xmlServletRegistrationBean
接收请求 → 查询 HandlerMapping → 获取 Handler → 调用 HandlerAdapter → 执行 Controller → 获取 ModelAndView → 解析 View → 渲染响应
HandlerMappingRequestMappingHandlerMapping(默认)请求映射器
根据请求 URL、Method、Header、Params 等信息,找到匹配的 Controller 方法(Handler)
- 基于注解 @RequestMapping
- 支持路径变量、通配符、正则
- 支持多级匹配(最精确优先)
被 DispatcherServlet 调用 → 返回 HandlerExecutionChain(含 Handler + 拦截器列表)
HandlerAdapterRequestMappingHandlerAdapter(默认)处理器适配器
调用 Handler(Controller 方法)
负责参数绑定、类型转换、数据校验、返回值处理
- 支持 @RequestMapping, @GetMapping
- 支持 @RequestBody, @ModelAttribute
- 支持异步返回 DeferredResult
- 支持 @ResponseBody 转 JSON
被 DispatcherServlet 调用 → 调用 Controller 方法 → 返回 ModelAndViewResponseEntity
Controller任意 @Controller / @RestController业务控制器
处理具体业务逻辑,返回模型数据和视图名(或直接返回响应体)
- 方法可返回 String(视图名)、ModelAndViewResponseEntity<T>voidMono<T>(WebFlux)等
- 方法参数支持 @RequestParam, @PathVariable, @RequestBody
被 HandlerAdapter 调用 → 执行业务 → 返回数据结构
ViewResolverThymeleafViewResolver, ContentNegotiatingViewResolver视图解析器
将 Controller 返回的逻辑视图名(如 "user/list")解析为物理视图对象(如 Thymeleaf 模板文件)
- 支持多种模板引擎(Thymeleaf, FreeMarker, JSP)
- 支持内容协商(Content Negotiation)
- 可配置优先级
被 DispatcherServlet 调用 → 根据视图名 + 请求 Accept 头 → 返回 View 对象
ViewThymeleafTemplate, MappingJackson2JsonView视图渲染器
将模型数据(Model)渲染成最终的响应内容(HTML、JSON、XML)
- 模板引擎渲染 HTML
- JSON 序列化器(Jackson)生成 JSON
- 可自定义 View 实现
被 DispatcherServlet 调用 → render() 方法执行 → 输出响应流

架构图(文字版)

HTTP Request
      ↓
┌──────────────────────┐
│ DispatcherServlet    │ ← 1. 接收请求
└──────────┬───────────┘
           ↓
┌──────────────────────┐
│ HandlerMapping       │ ← 2. 找到匹配的 Controller 方法
└──────────┬───────────┘
           ↓
┌──────────────────────┐
│ HandlerExecutionChain│ ← 3. 返回 Handler + 拦截器列表
└──────────┬───────────┘
           ↓
┌──────────────────────┐
│ HandlerAdapter       │ ← 4. 调用 Controller 方法
└──────────┬───────────┘
           ↓
┌──────────────────────┐
│     Controller       │ ← 5. 执行业务逻辑,返回 ModelAndView
└──────────┬───────────┘
           ↓
┌──────────────────────┐
│ ViewResolver         │ ← 6. 解析逻辑视图名 → 物理视图对象
└──────────┬───────────┘
           ↓
┌──────────────────────┐
│        View          │ ← 7. 渲染模型数据 → HTML/JSON
└──────────┬───────────┘
           ↓
┌──────────────────────┐
│ HTTP Response        │ ← 8. 返回客户端
└──────────────────────┘

关键设计哲学

  • 单一职责:每个组件只做一件事
  • 松耦合:组件间通过接口通信,可替换(如换 Jackson 为 Gson)
  • 可扩展:可自定义 HandlerMappingViewResolverHandlerAdapter
  • 统一入口:所有请求都经过 DispatcherServlet,便于统一处理(如日志、安全)

✅ 二、请求处理完整生命周期:从 HTTP 请求到响应返回(8步详解)

我们以一个标准的 REST API 请求为例:
GET /api/users/1001 → 返回 JSON

🔹 步骤 1:请求到达 DispatcherServlet

  • 触发者:Servlet 容器(Tomcat)接收到 HTTP 请求
  • 执行者DispatcherServlet
  • 动作
    • 初始化(若为首次请求)
    • 获取请求 URI:/api/users/1001
    • 获取 HTTP Method:GET
    • 获取请求头(Headers)、参数(Query)、Body
  • 关键代码入口
    // DispatcherServlet.doDispatch()
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // ...
    }
    

🔹 步骤 2:HandlerMapping 匹配 Controller 方法

  • 触发者DispatcherServlet 调用 HandlerMapping.getHandler()
  • 执行者RequestMappingHandlerMapping
  • 动作
    • 遍历所有注册的 @Controller 类及其方法
    • 匹配规则(按优先级):
      1. 精确路径匹配/api/users/1001
      2. 路径变量匹配/api/users/{id}
      3. 通配符匹配/api/users/**
      4. HTTP 方法匹配GET vs POST
      5. 请求头匹配Accept: application/json
      6. 参数匹配params="type=active"
    • 找到匹配方法:UserController.getUser(@PathVariable("id") Long id)
  • 输出HandlerExecutionChain
    • 包含:目标方法HandlerMethod 对象)
    • 包含:拦截器列表HandlerInterceptor[],按注册顺序)

路径匹配规则优先级(官方文档):

1. 精确路径(无变量) > 2. 路径变量 > 3. 通配符 > 4. 默认路径

🔹 步骤 3:执行 HandlerExecutionChain 中的拦截器 preHandle()

  • 触发者DispatcherServlet
  • 执行者:所有注册的 HandlerInterceptor
  • 动作
    • 按注册顺序,依次调用每个拦截器的 preHandle(request, response, handler)
    • 若任一拦截器返回 false请求被中断,跳过 Controller,直接调用 afterCompletion
    • 若全部返回 true → 继续执行
  • 典型用途
    • 登录校验(if (token == null) return false;
    • 请求日志记录(记录 startTime)
    • 性能监控(记录 startTime)
    • 跨域处理(设置 CORS 头)

拦截器执行顺序
Interceptor1.preHandle → Interceptor2.preHandle → ... → Controller → ... → Interceptor2.postHandle → Interceptor1.postHandle → ... → Interceptor1.afterCompletion → Interceptor2.afterCompletion

🔹 步骤 4:HandlerAdapter 调用 Controller 方法

  • 触发者DispatcherServlet
  • 执行者RequestMappingHandlerAdapter
  • 动作
    1. 解析方法签名:获取参数列表(@PathVariable, @RequestParam, @RequestBody 等)
    2. 参数解析器(ArgumentResolvers)
      • PathVariableMethodArgumentResolver → 提取 {id} → 转为 Long
      • RequestResponseBodyMethodProcessor → 读取 JSON Body → 反序列化为 User
      • RequestParamMethodArgumentResolver → 读取 query string
      • ModelMethodProcessor → 注入 Model
      • ServletWebRequestMethodArgumentResolver → 注入 HttpServletRequest
    3. 数据校验
      • 若参数有 @Valid → 调用 Validator(Hibernate Validator)
      • 校验失败 → 抛出 MethodArgumentNotValidException
    4. 类型转换
      • String "2025-01-01"LocalDate
      • String "admin"Role.ADMIN
    5. 调用目标方法
      // 伪代码
      Object result = method.invoke(controllerInstance, args);
      
    6. 返回值处理器(ReturnValueHandlers)
      • 若返回 String → 包装为 ModelAndView
      • 若返回 ResponseEntity<T> → 直接使用
      • 若返回 @ResponseBody 对象 → 用 HttpMessageConverter 转为 JSON
      • 若返回 void → 无响应体

核心机制
参数绑定 + 类型转换 + 数据校验 + 返回值序列化 全部由 HandlerAdapter 通过策略模式动态选择处理器完成。

🔹 步骤 5:Controller 执行业务逻辑

  • 执行者:开发者编写的 @Controller 类中的方法
  • 动作
    • 调用 Service 层(userService.findById(id)
    • 处理业务逻辑(权限校验、状态变更)
    • 构造返回数据:
      @GetMapping("/users/{id}")
      public ResponseEntity<User> getUser(@PathVariable Long id) {
          User user = userService.findById(id);
          if (user == null) {
              return ResponseEntity.notFound().build();
          }
          return ResponseEntity.ok(user); // ✅ 返回 ResponseEntity
      }
      
  • 返回值类型
    返回类型含义是否需要 @ResponseBody
    String逻辑视图名(如 "user/list"
    ModelAndView包含 Model + ViewName
    ResponseEntity<T>完整 HTTP 响应(状态码、头、体)✅(隐式)
    T + @ResponseBody直接序列化为 JSON/XML
    void无响应体✅(通常用于异步)
    Mono<T> / Flux<T>响应式编程✅(需 WebFlux)

最佳实践REST API 优先使用 ResponseEntity<T>,控制力最强。

🔹 步骤 6:ViewResolver 解析视图名

  • 触发者DispatcherServlet
  • 执行者ViewResolver(默认 ContentNegotiatingViewResolver
  • 动作
    1. 获取 Controller 返回的 逻辑视图名(如 "user/list"
    2. 获取客户端请求头:Accept: application/json / text/html
    3. 内容协商(Content Negotiation)
      • Accept: application/json → 选择 MappingJackson2JsonView
      • Accept: text/html → 选择 ThymeleafViewResolver
    4. 根据视图名 + 内容类型,查找物理视图
      • Thymeleaf:/WEB-INF/views/user/list.html
      • JSON:无物理文件,直接使用 HttpMessageConverter
    5. 返回 View 实例(如 JsonViewThymeleafTemplate

内容协商优先级(默认):

  1. URL 扩展(如 /user/1001.json
  2. 请求头 Accept
  3. 请求参数 ?format=json

🔹 步骤 7:View 渲染响应内容

  • 触发者DispatcherServlet
  • 执行者View 对象(如 MappingJackson2JsonView
  • 动作
    • 获取 Controller 返回的 Model 数据(如 User user = new User(...)
    • 调用 render(model, request, response)
    • 序列化
      • JSON:使用 ObjectMapper(Jackson) → user{"id":1001,"name":"张三"}
      • HTML:使用 Thymeleaf 引擎 → 渲染模板 → 生成完整 HTML 字符串
    • 写入 HttpServletResponse 的输出流(OutputStream)
  • 关键点
    • Model 数据 是一个 Map<String, Object>,键是变量名,值是对象
    • 渲染过程不涉及业务逻辑,仅是数据展示

🔹 步骤 8:执行拦截器 postHandle() 和 afterCompletion()

✅ 8.1 postHandle() —— 渲染前调用
  • 触发时机:Controller 执行完,View 渲染前
  • 执行者:所有拦截器,逆序执行(与 preHandle 顺序相反)
  • 用途
    • 修改 Model 数据(如添加通用变量)
    • 添加响应头(如 X-Powered-By
    • 记录处理时间(需在 preHandle 记录 startTime)
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    if (modelAndView != null) {
        modelAndView.addObject("appVersion", "v2.1.0"); // ✅ 添加全局变量
    }
}
✅ 8.2 afterCompletion() —— 响应完成后调用
  • 触发时机无论成功或异常,响应完全写入客户端后
  • 执行者:所有拦截器,逆序执行
  • 用途
    • 记录完整耗时(System.currentTimeMillis() - startTime
    • 记录访问日志(URL、IP、状态码、耗时)
    • 清理线程本地变量(如 RequestContextHolder
    • 发送监控指标(Prometheus)
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    Long startTime = (Long) request.getAttribute("startTime");
    if (startTime != null) {
        long duration = System.currentTimeMillis() - startTime;
        log.info("✅ [{}] {} {} | {}ms | Status: {}", 
            request.getMethod(), 
            request.getRequestURI(), 
            response.getStatus(), 
            duration, 
            request.getAttribute("requestId"));
    }
}

为什么必须逆序?
拦截器是栈结构preHandle 入栈,afterCompletion 出栈。
保证“先入后出”,如:日志记录先开始,后结束。


✅ 三、完整请求生命周期可视化流程图(Mermaid)

ClientDispatcherServletHandlerMappingHandlerAdapterControllerViewResolverViewInterceptor1Interceptor21. HTTP GET /api/users/10012. 查询匹配的 Handler3. 返回 HandlerExecutionChain (Handler + [Interceptor1, Interceptor2])4.1 preHandle()4.2 返回 true4.3 preHandle()4.4 返回 true5. 调用 HandlerAdapter6. 调用 UserController.getUser(id=1001)7. 返回 ResponseEntity<User>8. 返回 ModelAndView 或 ResponseEntity9. 解析视图名(此处为 JSON,无需视图)10. 返回 JsonView11. 调用 render()12. 返回 JSON 字符串13.1 postHandle()(逆序)13.2 完成13.3 postHandle()(逆序)13.4 完成14.1 afterCompletion()(逆序)14.2 记录耗时、日志14.3 afterCompletion()(逆序)14.4 记录耗时、日志15. 返回 HTTP 200 + JSONClientDispatcherServletHandlerMappingHandlerAdapterControllerViewResolverViewInterceptor1Interceptor2

图中关键点

  • 所有组件均由 DispatcherServlet 调用
  • 拦截器在 preHandleafterCompletion 中执行
  • postHandleView 渲染前执行
  • 无 View 的请求(REST API)直接由 HttpMessageConverter 输出

✅ 四、学习建议:如何真正掌握 Spring MVC?

阶段建议行动目标
1. 理解架构画出上述 5 组件图,手写每个组件的职责能口述“请求如何流转”
2. 源码追踪在 IDEA 中启动 Spring Boot,Debug DispatcherServlet.doDispatch()看清每一步调用栈
3. 模拟实现自己写一个简易版 MyDispatcherServlet,只支持 @RequestMapping + @ResponseBody理解核心逻辑
4. 调试拦截器写一个拦截器,打印 request.getRequestURI()response.getStatus()理解拦截器生命周期
5. 分析异常故意传非法参数(如 age=abc),看 MethodArgumentNotValidException 如何被处理理解异常传播链
6. 配置自定义替换 Jackson 为 Gson,自定义 ViewResolver理解可扩展性
7. 面试准备准备“请描述一个请求从浏览器到后端的完整流程”标准答案能讲 5 分钟不卡顿

推荐工具

  • IDEA:开启 Debug 模式,断点在 DispatcherServlet.doDispatch()
  • Postman / Apifox:测试不同 Accept 头、路径变量、JSON Body
  • Spring Boot Actuator/actuator/mappings 查看所有 URL 映射

✅ 五、使用 Spring MVC 的推荐规范与注意事项

✅ 推荐规范(企业级最佳实践)

类别规范说明
Controller 设计使用 @RestControllerREST API 统一用 @RestController,避免 @Controller + @ResponseBody 混用
请求映射优先使用 @GetMapping, @PostMapping@RequestMapping(method = GET) 更语义化
路径设计使用复数名词,如 /users,而非 /userRESTful 风格,符合惯例
参数绑定优先使用 @PathVariable@RequestParam@RequestBody禁止直接使用 request.getParameter()
数据校验使用 @Valid + JSR-380 注解所有 DTO 必须校验,禁止在 Controller 中手动校验
异常处理使用 @RestControllerAdvice 统一处理禁止每个 Controller 写 try-catch
响应格式统一返回 ResponseEntity<T>CommonResult<T>前后端契约清晰,避免 null 混淆
文件上传使用 MultipartFile,校验大小、类型生产环境使用 MinIO/OSS禁止直接存本地磁盘
拦截器用于登录、日志、性能监控禁止在拦截器中执行数据库查询(影响性能)
线程安全Controller 是单例,不要存储成员变量所有数据通过方法参数传递
性能优化开启 @EnableAsync + 异步写日志避免 I/O 阻塞主线程
内容协商明确指定 produces = "application/json"避免客户端误解析为 HTML

⚠️ 常见错误与避坑指南

错误原因正确做法
@Autowired 注入 HttpServletRequest虽然能用,但违反设计改用 @RequestParam, @RequestHeader
❌ 在 Controller 中调用 response.sendRedirect()跳过 View,破坏流程使用 return "redirect:/users"
@PathVariable 未校验传入 abc 导致 NumberFormatException@Min(1)@Pattern
@RequestBody 接收 null客户端没传 Body@NotNull@Valid
❌ 拦截器中 request.setAttribute() 传数据给 Controller不推荐,耦合高改用 @ModelAttribute@RequestAttribute
❌ 使用 @RequestMapping 没写 method可能被 POST 请求触发必须指定 method = GET
❌ 没有配置 ContentNegotiatingViewResolverJSON 返回乱码WebMvcConfigurer 中配置
❌ 使用 @Controller 返回 JSON 但没加 @ResponseBody返回视图名,不是 JSON改用 @RestController 或加注解
❌ 拦截器中调用 response.getWriter().write()会跳过后续组件只在 preHandlereturn false 中断

✅ 高级建议(进阶)

建议说明
使用 @ControllerAdvice + @ExceptionHandler 统一处理异常避免重复代码,提升可维护性
自定义 HttpMessageConverter如支持 Protobuf、YAML 格式
启用 @EnableWebMvc 时谨慎会覆盖 Spring Boot 自动配置,仅在需要完全控制时使用
使用 @RequestScope 管理请求级状态UserContext 存储当前登录用户
集成 Springdoc OpenAPI自动生成 Swagger UI 文档
使用 @CrossOrigin 或全局 CORS 配置解决前后端分离的跨域问题

✅ 六、总结:Spring MVC 的设计哲学

哲学体现
约定优于配置@RestController 默认返回 JSON,无需额外配置
松耦合组件间通过接口通信,可替换(如换 Jackson 为 Gson)
可扩展自定义 HandlerMappingViewResolverHandlerAdapter
统一入口所有请求走 DispatcherServlet,便于统一处理(安全、日志、监控)
声明式编程用注解声明“我要什么”,而不是“我怎么做”
关注点分离Controller 只管业务,校验归校验,视图归视图,异常归异常

终极目标
让开发者只写业务逻辑,其余交给框架。


✅ 七、学习路线图(进阶方向)

阶段学习内容
基础理解上述 5 组件 + 请求流程
进阶自定义 HandlerInterceptorViewResolverArgumentResolver
深入阅读 DispatcherServlet.javaRequestMappingHandlerAdapter.java 源码
实战搭建一个带权限校验、日志、上传下载、统一响应的完整项目
扩展学习 Spring WebFlux(响应式)、Spring GraphQL、Spring Cloud Gateway

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙茶清欢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值