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 中的处理遵循一条清晰的“请求-响应”管道。其完整流程如下图所示:
阶段一:请求接收与分发
- 用户发起请求: 用户通过浏览器访问一个 URL(例如
http://example.com/app/products
)。 - 请求到达
DispatcherServlet
:- HTTP 请求首先被 Web 容器(如 Tomcat)捕获。
- 根据
web.xml
或ServletRegistrationBean
中的配置,匹配到请求路径的DispatcherServlet
开始处理该请求。 DispatcherServlet
是唯一的入口,它代表“前端控制器”设计模式。
阶段二:寻找处理器
- 查询处理器映射(
HandlerMapping
):DispatcherServlet
会查询所有配置的HandlerMapping
bean(如RequestMappingHandlerMapping
),询问哪一个Handler
(控制器方法)能处理当前请求。HandlerMapping
根据请求的 URL、HTTP 方法(GET、POST等)、请求头等信息进行匹配。- 如果找到匹配的处理器,
HandlerMapping
会返回一个HandlerExecutionChain
对象。该对象不仅包含了目标处理器(Handler
),还包含了适用于该请求的所有HandlerInterceptor
(拦截器)。
阶段三:执行预处理拦截器
- 执行拦截器的
preHandle
方法:DispatcherServlet
按顺序执行HandlerExecutionChain
中所有拦截器的preHandle()
方法。- 拦截器通常用于执行权限检查、日志记录、本地化设置等预处理逻辑。
- 重要: 如果任何一个拦截器的
preHandle
方法返回false
,则流程中断,直接返回,不会执行后续的处理器和拦截器。
阶段四:执行处理器(业务逻辑)
- 获取并调用处理器适配器(
HandlerAdapter
):DispatcherServlet
遍历所有配置的HandlerAdapter
,找到第一个支持该类型处理器的适配器(如RequestMappingHandlerAdapter
用于支持@RequestMapping
注解的方法)。
- 实际执行处理器:
- 适配器调用处理器的具体方法(即我们编写的
@Controller
中的方法),并传入相应的参数(如@RequestParam
,@PathVariable
,@RequestBody
等注解修饰的参数)。参数解析由一系列的HandlerMethodArgumentResolver
完成。 - 处理器执行业务逻辑(如调用 Service 层),并返回一个结果。这个结果通常被包装成一个
ModelAndView
对象(包含模型数据和视图名),或者只是一个视图名,或者是一个被@ResponseBody
注解的对象。
- 适配器调用处理器的具体方法(即我们编写的
阶段五:执行后处理拦截器
- 执行拦截器的
postHandle
方法:- 处理器执行完毕后,
DispatcherServlet
按逆序执行所有拦截器的postHandle()
方法。 - 此时可以对
ModelAndView
进行进一步的修改(如添加公共模型数据),但在前后端分离架构中(返回 JSON),此方法可能用处不大。
- 处理器执行完毕后,
阶段六:处理结果与渲染视图
-
处理返回结果:
- 如果处理器方法返回的是
String
(视图名)或ModelAndView
,DispatcherServlet
会将其转发给ViewResolver
。 ViewResolver
根据逻辑视图名解析出具体的View
对象(例如,将"products"
解析为/WEB-INF/views/products.jsp
)。- 如果方法有
@ResponseBody
注解或控制器有@RestController
注解,则结果会通过HttpMessageConverter
直接写入 HTTP 响应体(返回 JSON/XML 等),跳过后面的视图渲染步骤。
- 如果处理器方法返回的是
-
渲染视图(如果需要):
DispatcherServlet
将模型数据传递给解析得到的View
对象,并调用其render()
方法。View
负责将模型数据与模板结合,生成最终的响应内容(如 HTML)。
阶段七:请求完成回调
- 执行拦截器的
afterCompletion
方法:- 无论请求处理成功与否,在返回响应给客户端之前,
DispatcherServlet
会按逆序执行所有拦截器的afterCompletion()
方法。 - 此方法非常适合进行资源清理、记录请求完成日志等操作。注意:即使前面的
preHandle
或处理器执行过程中抛出异常,此方法也会被调用(但仅限那些preHandle
成功执行并返回true
的拦截器)。
- 无论请求处理成功与否,在返回响应给客户端之前,
阶段八:返回响应
- 返回响应: 最终生成的响应(可能是 HTML 页面,也可能是 JSON 数据)通过
DispatcherServlet
返回给 Web 容器,并由容器最终发送回客户端。
4. 异常处理机制
异常处理贯穿于上述流程的多个环节:
- 处理器执行期间: 如果在任何阶段(特别是处理器执行时)抛出异常,
DispatcherServlet
会捕获它。 - 委托给
HandlerExceptionResolver
:DispatcherServlet
会遍历所有配置的HandlerExceptionResolver
(如@ExceptionHandler
,ExceptionHandlerExceptionResolver
),让它们来处理异常。 - 解析异常: 异常解析器可能会将异常转换为一个错误响应(如设置特定的 HTTP 状态码,返回一个错误视图
ModelAndView("error")
,或者直接写入一个 JSON 错误信息)。 - 跳过后续步骤: 一旦发生异常,正常的流程(如
postHandle
)会被跳过,但afterCompletion
仍然会执行。
5. 总结与要点
- 中心化控制:
DispatcherServlet
是流程的绝对核心,是所有请求的交通枢纽。 - 职责分离: 每个组件职责单一(映射、适配、执行、解析),符合设计模式的开闭原则,使得框架高度可配置和可扩展。
- 扩展点: 开发者可以通过实现或扩展
HandlerInterceptor
,HandlerAdapter
,ViewResolver
,HandlerExceptionResolver
等接口来定制流程的特定环节。 - 两种主要结果处理方式:
- 传统 MVC: 处理器返回视图名 ->
ViewResolver
解析 ->View
渲染。 - RESTful API: 处理器返回数据 +
@ResponseBody
->HttpMessageConverter
转换 -> 直接写入响应。
- 传统 MVC: 处理器返回视图名 ->