Spring MVC 请求执行流程详解

1. 核心组件简介

在深入了解流程之前,首先需要认识流程中涉及的核心组件:

  • DispatcherServlet前端控制器(Front Controller),是整个流程的核心和入口。它接收所有请求,并负责协调各个组件共同完成请求处理,但其自身不处理业务逻辑。
  • HandlerMapping处理器映射器 ,根据请求的 URL、方法等信息,找到能够处理该请求的处理器(Handler)拦截器(Interceptors)
  • HandlerAdapter处理器适配器 ,负责以统一的接口方式去实际执行找到的处理器(如 @Controller 中的方法)。因为处理器有多种形式(如基于注解的控制器、实现 Controller 接口的控制器等),适配器模式使得 DispatcherServlet 无需关心处理器的具体实现。
  • HandlerInterceptor处理器拦截器,提供了在处理器执行前、后以及请求完成后的拦截钩子,用于实现横切关注点(如日志、权限验证等)。
  • Handler处理器 ,通常是我们编写的带有 @Controller@RestController 注解的类中的方法,是实际执行业务逻辑的地方。
  • ViewResolver视图解析器 ,根据控制器返回的逻辑视图名(如 "home"),解析出具体的 View 对象(如 JSP, Thymeleaf 模板等)。
  • View视图,负责将模型数据渲染成最终的响应内容(如 HTML 页面)。
  • HandlerExceptionResolver异常解析器,负责处理请求处理过程中抛出的异常,将其转换为统一的错误响应(如错误页面、JSON 错误信息)。
  • MultipartResolver: ** multipart 解析器**,用于解析文件上传等 multipart 类型的请求。

2. 请求执行流程详述

一个典型的 HTTP 请求在 Spring MVC 中的处理遵循一条清晰的"请求-响应"管道。其完整流程如下图所示:

阶段一:请求接收与分发

  1. 用户发起请求 : 用户通过浏览器访问一个 URL(例如 http://example.com/app/products)。
  2. 请求到达 DispatcherServlet
    • HTTP 请求首先被 Web 容器(如 Tomcat)捕获。
    • 根据 web.xmlServletRegistrationBean 中的配置,匹配到请求路径的 DispatcherServlet 开始处理该请求。
    • DispatcherServlet 是唯一的入口,它代表"前端控制器"设计模式。

阶段二:寻找处理器

  1. 查询处理器映射(HandlerMapping
    • DispatcherServlet 会查询所有配置的 HandlerMapping bean(如 RequestMappingHandlerMapping),询问哪一个 Handler(控制器方法)能处理当前请求。
    • HandlerMapping 根据请求的 URL、HTTP 方法(GET、POST等)、请求头等信息进行匹配。
    • 如果找到匹配的处理器,HandlerMapping 会返回一个 HandlerExecutionChain 对象。该对象不仅包含了目标处理器(Handler),还包含了适用于该请求的所有 HandlerInterceptor(拦截器)。

阶段三:执行预处理拦截器

  1. 执行拦截器的 preHandle 方法
    • DispatcherServlet 按顺序执行 HandlerExecutionChain 中所有拦截器的 preHandle() 方法。
    • 拦截器通常用于执行权限检查、日志记录、本地化设置等预处理逻辑。
    • 重要 : 如果任何一个拦截器的 preHandle 方法返回 false,则流程中断,直接返回,不会执行后续的处理器和拦截器。

阶段四:执行处理器(业务逻辑)

  1. 获取并调用处理器适配器(HandlerAdapter
    • DispatcherServlet 遍历所有配置的 HandlerAdapter,找到第一个支持该类型处理器的适配器(如 RequestMappingHandlerAdapter 用于支持 @RequestMapping 注解的方法)。
  2. 实际执行处理器
    • 适配器调用处理器的具体方法(即我们编写的 @Controller 中的方法),并传入相应的参数(如 @RequestParam, @PathVariable, @RequestBody 等注解修饰的参数)。参数解析由一系列的 HandlerMethodArgumentResolver 完成。
    • 处理器执行业务逻辑(如调用 Service 层),并返回一个结果。这个结果通常被包装成一个 ModelAndView 对象(包含模型数据和视图名),或者只是一个视图名,或者是一个被 @ResponseBody 注解的对象。

阶段五:执行后处理拦截器

  1. 执行拦截器的 postHandle 方法
    • 处理器执行完毕后,DispatcherServlet逆序 执行所有拦截器的 postHandle() 方法。
    • 此时可以对 ModelAndView 进行进一步的修改(如添加公共模型数据),但在前后端分离架构中(返回 JSON),此方法可能用处不大。

阶段六:处理结果与渲染视图

  1. 处理返回结果

    • 如果处理器方法返回的是 String(视图名)或 ModelAndViewDispatcherServlet 会将其转发给 ViewResolver
    • ViewResolver 根据逻辑视图名解析出具体的 View 对象(例如,将 "products" 解析为 /WEB-INF/views/products.jsp)。
    • 如果方法有 @ResponseBody 注解或控制器有 @RestController 注解,则结果会通过 HttpMessageConverter 直接写入 HTTP 响应体(返回 JSON/XML 等),跳过后面的视图渲染步骤。
  2. 渲染视图(如果需要)

    • DispatcherServlet 将模型数据传递给解析得到的 View 对象,并调用其 render() 方法。
    • View 负责将模型数据与模板结合,生成最终的响应内容(如 HTML)。

阶段七:请求完成回调

  1. 执行拦截器的 afterCompletion 方法
    • 无论请求处理成功与否,在返回响应给客户端之前DispatcherServlet 会按逆序 执行所有拦截器的 afterCompletion() 方法。
    • 此方法非常适合进行资源清理、记录请求完成日志等操作。注意 :即使前面的 preHandle 或处理器执行过程中抛出异常,此方法也会被调用(但仅限那些 preHandle 成功执行并返回 true 的拦截器)。

阶段八:返回响应

  1. 返回响应 : 最终生成的响应(可能是 HTML 页面,也可能是 JSON 数据)通过 DispatcherServlet 返回给 Web 容器,并由容器最终发送回客户端。

4. 异常处理机制

异常处理贯穿于上述流程的多个环节:

  • 处理器执行期间 : 如果在任何阶段(特别是处理器执行时)抛出异常,DispatcherServlet 会捕获它。
  • 委托给 HandlerExceptionResolverDispatcherServlet 会遍历所有配置的 HandlerExceptionResolver(如 @ExceptionHandler, ExceptionHandlerExceptionResolver),让它们来处理异常。
  • 解析异常 : 异常解析器可能会将异常转换为一个错误响应(如设置特定的 HTTP 状态码,返回一个错误视图 ModelAndView("error"),或者直接写入一个 JSON 错误信息)。
  • 跳过后续步骤 : 一旦发生异常,正常的流程(如 postHandle)会被跳过,但 afterCompletion 仍然会执行。

5. 总结与要点

  • 中心化控制DispatcherServlet 是流程的绝对核心,是所有请求的交通枢纽。
  • 职责分离: 每个组件职责单一(映射、适配、执行、解析),符合设计模式的开闭原则,使得框架高度可配置和可扩展。
  • 扩展点 : 开发者可以通过实现或扩展 HandlerInterceptor, HandlerAdapter, ViewResolver, HandlerExceptionResolver 等接口来定制流程的特定环节。
  • 两种主要结果处理方式
    1. 传统 MVC : 处理器返回视图名 -> ViewResolver 解析 -> View 渲染。
    2. RESTful API : 处理器返回数据 + @ResponseBody -> HttpMessageConverter 转换 -> 直接写入响应。
相关推荐
_星辰大海乀5 小时前
初识网络原理
java·网络·协议·tcp协议·osi·ip协议·封装分用
_extraordinary_5 小时前
Java HTTP协议(一)--- HTTP,报文格式,请求和响应
java·开发语言·http
蜚鸣5 小时前
JavaStream用法全解析
java
失散135 小时前
分布式专题——19 Zookeeper分布式一致性协议ZAB源码剖析
java·分布式·zookeeper·云原生·架构
间彧5 小时前
Collections.singletonList详解与应用
java
代码匠心1 天前
从零开始学Flink:数据转换的艺术
java·大数据·flink
泥嚎泥嚎3 天前
【Android】View 的滑动
java
京茶吉鹿3 天前
三步构建完美树节点,从此告别数据结构焦虑!
java·后端
橙序员小站3 天前
搞定系统设计题:如何设计一个订单系统?
java·后端·面试