介绍一下Spring MVC的执行流程

Spring MVC 的执行流程是一个高度协同的组件协作过程,其核心围绕 DispatcherServlet(前端控制器) 展开,通过多个组件协同完成请求处理。以下是详细执行流程,结合传统 MVC 和现代前后端分离场景说明:

一、核心执行流程(11步详解)

  1. 请求接收(DispatcherServlet 拦截)

    • 用户发送 HTTP 请求至服务器,由DispatcherServlet统一捕获。它是 Spring MVC 的入口,作为前端控制器模式的核心,负责请求分发和响应调度。

  2. 处理器映射(HandlerMapping 查找 Handler)

    • DispatcherServlet调用HandlerMapping,根据请求 URL 解析对应的处理(Handler)。返回HandlerExecutionChain对象,包含目标处理器(如@Controller方法)和关联的拦截器(Interceptor)。

  3. 拦截器预处理(Interceptor#preHandle)

    • 执行HandlerExecutionChain中所有拦截器的preHandle()方法。若任一拦截器返回false,流程终止并直接返回响应(如权限校验失败)。

  4. 处理器适配与执行(HandlerAdapter 调用 Handler)

    • DispatcherServlet通过HandlerAdapter适配并执行处理器方法(如Controller中的@RequestMapping方法)。适配器负责关键操作:

      • 参数绑定:将 HTTP 请求参数转换为方法参数(@RequestParam@RequestBody)。

      • 数据转换/格式化:如字符串转日期、数字类型。

      • 数据验证:通过Validator校验参数合法性。

  5. 处理器执行业务逻辑

    • 处理器方法调用 Service 层执行业务逻辑(如数据库操作),并准备模型数(Model)。

  6. 处理器返回结果

    • 处理器返回ModelAndView(传统 MVC 模式,含模型数据和视图名称)或直接返回数据对象(前后端分离场景)。后者通过HttpMessageConverter(如MappingJackson2HttpMessageConverter)序列化为 JSON,跳过视图解析步骤。

  7. 拦截器后处理(Interceptor#postHandle)

    • 执行拦截器的postHandle()方法,可修改ModelAndView(仅对传统 MVC 有效)。

  8. 视图解析(ViewResolver 解析视图)

    • DispatcherServlet将ModelAndView传递给ViewResolver,将逻辑视图(如"userList")解析为实际视图(如 JSP、Thymeleaf 模板)。

  9. 视图渲染(View 渲染)

    • 视图对象(如 InternalResourceView)将模型数据填充到模板中,生成最终响应内容(如 HTML)。

    • 示例:JSP 页面通过 EL 表达式${message}渲染模型数据。

  10. 响应返回客户端

    • 渲染后的内容通过DispatcherServlet返回客户端。

  11. 拦截器收尾(Interceptor#afterCompletion)

    • 无论请求成功或异常,最终执行拦截器的afterCompletion()方法,用于资源清理(如关闭数据库连接)。

二、前后端分离场景的差异

步骤传统 MVC前后端分离
处理器返回结果返回 ModelAndView(视图+数据)直接返回数据对象(如 DTO)
视图解析与渲染需要 ViewResolverView 渲染 HTML跳过此步骤,由 HttpMessageConverter 序列化为 JSON
响应格式HTML 页面JSON/XML 等数据格式

三、核心组件职责

组件作用示例
DispatcherServlet前端控制器,统一分发请求与响应拦截所有请求,协调各组件协作
HandlerMapping映射 URL 到处理器(HandlerRequestMappingHandlerMapping 解析 @RequestMapping 注解
HandlerAdapter适配不同类型处理器(如 @Controller 方法),执行参数绑定与验证RequestMappingHandlerAdapter 处理注解驱动的控制器
ViewResolver将逻辑视图名解析为实际视图InternalResourceViewResolver 解析 JSP 路径(前缀 /WEB-INF/views/ + 后缀 .jsp
InterceptorAOP 机制实现横切逻辑(如日志、权限)自定义拦截器实现 preHandle() 校验登录状态
HttpMessageConverter序列化/反序列化请求与响应数据(前后端分离)MappingJackson2HttpMessageConverter 处理 JSON

四、关键设计思想

  1. 前端控制器模式DispatcherServlet作为唯一入口,解耦请求分发与业务处理,统一处理流程。

  2. 组件可插拔各组件(如ViewResolver)通过接口定义,支持灵活替换(如从 JSP 切换至 Thymeleaf)。

  3. 拦截器链通过Interceptor实现横切关注点,增强扩展性(如全局日志、安全校验)。

  4. 适配器模式HandlerAdapter屏蔽处理器差异,支持多种控制器类型(如基于注解或实现

    Controller接口)。

五、常见误区与避坑

  1. 视图解析跳过条件若处理器使用@ResponseBody或@RestController,直接返回数据,跳过视图解析。

  2. 拦截器执行顺序preHandle()→ 处理器执行 →postHandle()→ 视图渲染→afterCompletion()。

  3. 参数绑定原理请求体(如 JSON)通过HttpMessageConverter绑定到@RequestBody参数,表单数据则通过DataBinder转换。

六、示例流程(用户访问 /user/list

  1. DispatcherServlet 拦截请求。

  2. HandlerMapping 找到 UserController#list() 方法。

  3. HandlerAdapter 绑定请求参数,调用 list() 方法。

  4. list() 查询数据库,返回 ModelAndView("userList", userList)

  5. ViewResolver 解析 "userList"/WEB-INF/views/userList.jsp

  6. JSP 视图渲染 userList 数据为 HTML。

  7. DispatcherServlet返回 HTML 响应。

详细流程

Handler返回ModelAndView

1. Controller方法执行业务逻辑
  • 场景:用户访问 /user/listUserController 中的 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
  • 传递流程HandlerAdapterDispatcherServlet

  • 数据结构:

    • Model:存储业务数据(如 userList 用户列表)。

    • View:逻辑视图名(如"userList"),需由ViewResolver解析为实际视图。

关联流程:后续步骤如何消费ModelAndView

  1. 视图解析(第8步)

    • DispatcherServlet调用ViewResolver解析逻辑视图名:

      // 配置示例:InternalResourceViewResolver
      resolver.setPrefix("/WEB-INF/views/"); // 路径前缀
      resolver.setSuffix(".jsp");            // 文件后缀[4](@ref)
      • 解析结果:"userList"→/WEB-INF/views/userList.jsp

  2. 视图渲染(第10步)

    • 数据填充:将Model中的数据注入视图(如JSP通过request.setAttribute("userList", users))。

    • 渲染结果:生成HTML响应(例如JSP中使用 <c:forEach> 遍历 userList)。


💡 技术细节与常见问题

  1. 为何需要适配器?

    • 解耦设计:HandlerAdapter屏蔽了不同类型Controller的差异(如基于注解 vs. 实现

      Controller接口),统一调用逻辑。

  2. 返回非视图数据(如JSON)

    • 若方法标注@ResponseBody:

      • 跳过视图解析,由HttpMessageConverter将返回值序列化为JSON(如

        MappingJackson2HttpMessageConverter)。

      • 示例:

        @GetMapping("/api/users")
        @ResponseBody
        public List<User> getUsers() {
            return userService.findAll(); // 直接返回JSON
        }
  3. 异常处理

    • 若Controller抛出异常:

      • HandlerAdapter捕获异常并封装为ModelAndView(含错误视图名)。

      • 或由@ExceptionHandler直接返回错误JSON。

    完整示例流程(/user/list 请求)

    步骤组件操作输出
    1-5DispatcherServletHandlerMappingHandlerAdapter路由至 UserController#list()调用Controller方法
    6UserController执行查询,返回 "userList" + 模型数据ModelAndView("userList", {userList: [...]})
    7HandlerAdapter封装结果返回 ModelAndViewDispatcherServlet
    8-10ViewResolverView解析视图 → 渲染HTML生成用户列表页面

注意事项

  1. 避免模型数据污染:确保不同请求的模型数据独立(单例Controller需避免使用成员变量存储模型)。

  2. 性能优化:

    • 返回视图名而非ModelAndView对象可减少内存开销(适配器自动封装)。

    • 频繁调用的接口建议使用@ResponseBody跳过视图解析。

流程意义:第六步是 业务逻辑与视图渲染的桥梁,通过标准化 ModelAndView 对象,Spring MVC 实现了控制器与视图技术的完全解耦,支持灵活切换视图引擎(JSP/Thymeleaf等)


总结:Spring MVC 通过 DispatcherServlet 协调各组件,将请求处理分为 映射→执行→渲染 三个阶段,兼顾灵活性与扩展性。理解流程细节有助于优化 Web 层设计(如精准配置拦截器、选择视图技术),并快速定位请求处理中的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

tsxchen

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

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

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

打赏作者

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

抵扣说明:

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

余额充值