Spring MVC 的执行流程是一个高度协同的组件协作过程,其核心围绕 DispatcherServlet(前端控制器) 展开,通过多个组件协同完成请求处理。以下是详细执行流程,结合传统 MVC 和现代前后端分离场景说明:
一、核心执行流程(11步详解)
-
请求接收(DispatcherServlet 拦截)
-
用户发送 HTTP 请求至服务器,由DispatcherServlet统一捕获。它是 Spring MVC 的入口,作为前端控制器模式的核心,负责请求分发和响应调度。
-
-
处理器映射(HandlerMapping 查找 Handler)
-
DispatcherServlet调用HandlerMapping,根据请求 URL 解析对应的处理(Handler)。返回HandlerExecutionChain对象,包含目标处理器(如@Controller方法)和关联的拦截器(Interceptor)。
-
-
拦截器预处理(Interceptor#preHandle)
-
执行HandlerExecutionChain中所有拦截器的preHandle()方法。若任一拦截器返回false,流程终止并直接返回响应(如权限校验失败)。
-
-
处理器适配与执行(HandlerAdapter 调用 Handler)
-
DispatcherServlet通过HandlerAdapter适配并执行处理器方法(如Controller中的@RequestMapping方法)。适配器负责关键操作:
-
参数绑定:将 HTTP 请求参数转换为方法参数(
@RequestParam@RequestBody)。 -
数据转换/格式化:如字符串转日期、数字类型。
-
数据验证:通过Validator校验参数合法性。
-
-
-
处理器执行业务逻辑
-
处理器方法调用 Service 层执行业务逻辑(如数据库操作),并准备模型数(Model)。
-
-
处理器返回结果
-
处理器返回ModelAndView(传统 MVC 模式,含模型数据和视图名称)或直接返回数据对象(前后端分离场景)。后者通过HttpMessageConverter(如MappingJackson2HttpMessageConverter)序列化为 JSON,跳过视图解析步骤。
-
-
拦截器后处理(Interceptor#postHandle)
-
执行拦截器的postHandle()方法,可修改ModelAndView(仅对传统 MVC 有效)。
-
-
视图解析(ViewResolver 解析视图)
-
DispatcherServlet将ModelAndView传递给ViewResolver,将逻辑视图(如"userList")解析为实际视图(如 JSP、Thymeleaf 模板)。
-
-
视图渲染(View 渲染)
-
视图对象(如
InternalResourceView)将模型数据填充到模板中,生成最终响应内容(如 HTML)。 -
示例:JSP 页面通过 EL 表达式${message}渲染模型数据。
-
-
响应返回客户端
-
渲染后的内容通过DispatcherServlet返回客户端。
-
-
拦截器收尾(Interceptor#afterCompletion)
-
无论请求成功或异常,最终执行拦截器的afterCompletion()方法,用于资源清理(如关闭数据库连接)。
-
二、前后端分离场景的差异
| 步骤 | 传统 MVC | 前后端分离 |
|---|---|---|
| 处理器返回结果 | 返回 ModelAndView(视图+数据) | 直接返回数据对象(如 DTO) |
| 视图解析与渲染 | 需要 ViewResolver 和 View 渲染 HTML | 跳过此步骤,由 HttpMessageConverter 序列化为 JSON |
| 响应格式 | HTML 页面 | JSON/XML 等数据格式 |
三、核心组件职责
| 组件 | 作用 | 示例 |
|---|---|---|
DispatcherServlet | 前端控制器,统一分发请求与响应 | 拦截所有请求,协调各组件协作 |
HandlerMapping | 映射 URL 到处理器(Handler) | RequestMappingHandlerMapping 解析 @RequestMapping 注解 |
HandlerAdapter | 适配不同类型处理器(如 @Controller 方法),执行参数绑定与验证 | RequestMappingHandlerAdapter 处理注解驱动的控制器 |
ViewResolver | 将逻辑视图名解析为实际视图 | InternalResourceViewResolver 解析 JSP 路径(前缀 /WEB-INF/views/ + 后缀 .jsp) |
Interceptor | AOP 机制实现横切逻辑(如日志、权限) | 自定义拦截器实现 preHandle() 校验登录状态 |
HttpMessageConverter | 序列化/反序列化请求与响应数据(前后端分离) | MappingJackson2HttpMessageConverter 处理 JSON |
四、关键设计思想
-
前端控制器模式DispatcherServlet作为唯一入口,解耦请求分发与业务处理,统一处理流程。
-
组件可插拔各组件(如ViewResolver)通过接口定义,支持灵活替换(如从 JSP 切换至 Thymeleaf)。
-
拦截器链通过Interceptor实现横切关注点,增强扩展性(如全局日志、安全校验)。
-
适配器模式HandlerAdapter屏蔽处理器差异,支持多种控制器类型(如基于注解或实现
Controller接口)。
五、常见误区与避坑
-
视图解析跳过条件若处理器使用@ResponseBody或@RestController,直接返回数据,跳过视图解析。
-
拦截器执行顺序preHandle()→ 处理器执行 →postHandle()→ 视图渲染→afterCompletion()。
-
参数绑定原理请求体(如 JSON)通过HttpMessageConverter绑定到@RequestBody参数,表单数据则通过DataBinder转换。
六、示例流程(用户访问 /user/list)
-
DispatcherServlet拦截请求。 -
HandlerMapping找到UserController#list()方法。 -
HandlerAdapter绑定请求参数,调用list()方法。 -
list()查询数据库,返回ModelAndView("userList", userList)。 -
ViewResolver解析"userList"为/WEB-INF/views/userList.jsp。 -
JSP 视图渲染
userList数据为 HTML。 -
DispatcherServlet返回 HTML 响应。
详细流程
Handler返回ModelAndView
1. Controller方法执行业务逻辑
-
场景:用户访问
/user/list,UserController中的list()方法被调用。 -
关键操作:
-
调用UserService查询用户列表(如List<User> users = userService.findAll())。
-
将数据存入模型(Model model.addAttribute("userList", users))。
-
-
返回对象:
-
返回逻辑视图名(如return "userList"),或直接返回ModelAndView对象(new ModelAndView("userList", "userList", users))。
-
2. 处理器适配器接收结果
-
适配器角色:HandlerAdapter(如RequestMappingHandlerAdapter)接收Controller返回的结果:
-
若返回String(视图名),适配器自动封装为ModelAndView(视图名 + 模型数据)。
-
若返回ModelAndView,直接传递至下一步。
-
-
数据封装逻辑:
// HandlerAdapter内部伪代码 ModelAndView mv = new ModelAndView(); mv.setViewName(handlerMethod.getReturnValue()); // 视图名 mv.addAllAttributes(model); // 模型数据
3. 适配器返回ModelAndView给DispatcherServlet
-
传递流程:
HandlerAdapter→DispatcherServlet -
数据结构:
-
Model:存储业务数据(如
userList用户列表)。 -
View:逻辑视图名(如"userList"),需由ViewResolver解析为实际视图。
-
关联流程:后续步骤如何消费ModelAndView
-
视图解析(第8步)
-
DispatcherServlet调用ViewResolver解析逻辑视图名:
// 配置示例:InternalResourceViewResolver resolver.setPrefix("/WEB-INF/views/"); // 路径前缀 resolver.setSuffix(".jsp"); // 文件后缀[4](@ref)-
解析结果:"userList"→/WEB-INF/views/userList.jsp
-
-
-
视图渲染(第10步)
-
数据填充:将Model中的数据注入视图(如JSP通过request.setAttribute("userList", users))。
-
渲染结果:生成HTML响应(例如JSP中使用
<c:forEach>遍历userList)。
-
💡 技术细节与常见问题
-
为何需要适配器?
-
解耦设计:HandlerAdapter屏蔽了不同类型Controller的差异(如基于注解 vs. 实现
Controller接口),统一调用逻辑。
-
-
返回非视图数据(如JSON)
-
若方法标注@ResponseBody:
-
跳过视图解析,由HttpMessageConverter将返回值序列化为JSON(如
MappingJackson2HttpMessageConverter)。
-
示例:
@GetMapping("/api/users") @ResponseBody public List<User> getUsers() { return userService.findAll(); // 直接返回JSON }
-
-
-
异常处理
-
若Controller抛出异常:
-
HandlerAdapter捕获异常并封装为ModelAndView(含错误视图名)。
-
或由@ExceptionHandler直接返回错误JSON。
-
完整示例流程(/user/list 请求)
步骤 组件 操作 输出 1-5 DispatcherServlet→HandlerMapping→HandlerAdapter路由至 UserController#list()调用Controller方法 6 UserController执行查询,返回 "userList"+ 模型数据ModelAndView("userList", {userList: [...]})7 HandlerAdapter封装结果 返回 ModelAndView至DispatcherServlet8-10 ViewResolver→View解析视图 → 渲染HTML 生成用户列表页面 -
注意事项
-
避免模型数据污染:确保不同请求的模型数据独立(单例Controller需避免使用成员变量存储模型)。
-
性能优化:
-
返回视图名而非ModelAndView对象可减少内存开销(适配器自动封装)。
-
频繁调用的接口建议使用@ResponseBody跳过视图解析。
-
流程意义:第六步是 业务逻辑与视图渲染的桥梁,通过标准化
ModelAndView对象,Spring MVC 实现了控制器与视图技术的完全解耦,支持灵活切换视图引擎(JSP/Thymeleaf等)
总结:Spring MVC 通过 DispatcherServlet 协调各组件,将请求处理分为 映射→执行→渲染 三个阶段,兼顾灵活性与扩展性。理解流程细节有助于优化 Web 层设计(如精准配置拦截器、选择视图技术),并快速定位请求处理中的问题。

1366

被折叠的 条评论
为什么被折叠?



