当前位置: 首页 > news >正文

Spring MVC 处理请求的流程

Spring MVC 处理请求的流程

      • 流程步骤详解
        • 第1步:发起请求 (HTTP Request)
        • 第2步:映射处理器 (Handler Mapping)
        • 第3步:获取适配器 (Handler Adapter)
        • 第4步:执行拦截器前置处理 (Interceptors - preHandle)
        • 第5步:真正调用处理器 (Handler Execution)
        • 第6步:执行拦截器后置处理 (Interceptors - postHandle)
        • 第7步:处理分发结果 (Process Dispatch Result)
        • 第8步:执行拦截器完成处理 (Interceptors - afterCompletion)
        • 第9步:返回响应 (HTTP Response)
      • 核心组件总结

Spring MVC 处理请求的流程是其最核心的机制,理解它对于掌握整个框架至关重要。这个过程围绕一个核心——前端控制器模式(Front Controller Pattern),即 DispatcherServlet。

整个流程清晰且模块化,下图展示了从请求发起到响应返回的完整生命周期,以及其中涉及的核心组件交互:

ClientDispatcherServlet(前端控制器)HandlerMappingHandlerAdapterController (@Controller)ViewResolverView (JSP/Thymeleaf...)HTTP Request1. 接收请求查询Handler2. 映射查找根据URL找到目标Controller和方法返回HandlerExecutionChain(包含Handler和Interceptors)3. 适配调用获取HandlerAdapter返回Adapter按顺序执行preHandle()loop[4. 执行拦截器 (preHandle)]5. 处理请求(参数解析、方法调用、返回值处理)调用Controller方法返回方法执行结果(ModelAndView/String等)逆序执行postHandle()loop[6. 执行拦截器 (postHandle)]7. 处理视图/响应通过HttpMessageConverter直接写回响应解析视图名返回View对象调用render()渲染视图返回渲染后的HTMLalt[返回@ResponseBody][返回视图名]最终触发afterCompletion()loop[8. 执行拦截器 (afterCompletion)]9. 返回响应HTTP ResponseClientDispatcherServlet(前端控制器)HandlerMappingHandlerAdapterController (@Controller)ViewResolverView (JSP/Thymeleaf...)

流程步骤详解

现在,我们结合上图,对每一个步骤进行详细解读:

第1步:发起请求 (HTTP Request)

请求离开浏览器,到达 DispatcherServlet。根据 web.xml 或 Servlet 3.0+ 的配置,所有匹配特定模式(如 /)的请求都会由它处理。

第2步:映射处理器 (Handler Mapping)

DispatcherServlet 咨询一个或多个 HandlerMapping Bean:“这个请求应该由哪个‘处理器’(Handler)来处理?”。HandlerMapping 根据请求的 URL 进行查找。

  • 常见实现
    • RequestMappingHandlerMapping:用于映射 @RequestMapping 注解的方法(最常用)。
    • BeanNameUrlHandlerMapping:根据 Bean 的名字进行映射。
  • 返回结果:不仅返回找到的目标处理器方法,还会返回一个 HandlerExecutionChain 对象,该对象包含了找到的 Handler 以及所有适用于该请求的 HandlerInterceptor(拦截器)。
第3步:获取适配器 (Handler Adapter)

DispatcherServlet 拿着找到的 Handler,问一堆 HandlerAdapter:“你们谁支持(support)这个 Handler 的类型?”

  • 为什么需要它?:Handler 的类型五花八门(如 @ControllerHttpRequestHandlerServlet)。DispatcherServlet 需要一個统一的接口来调用它们。HandlerAdapter适配器模式的典型应用,它屏蔽了不同处理器的调用细节。
  • 常见实现RequestMappingHandlerAdapter(用于适配 @RequestMapping 注解的方法)。
第4步:执行拦截器前置处理 (Interceptors - preHandle)

在真正调用业务逻辑之前,DispatcherServlet 会调用 HandlerExecutionChain 中所有拦截器的 preHandle() 方法。

  • 应用:进行权限检查、日志记录、 locale 解析等。
第5步:真正调用处理器 (Handler Execution)

现在,DispatcherServlet 让获取到的 HandlerAdapter 去真正地执行 Handler。
这个执行过程非常复杂,包括:

  1. 参数解析:根据方法签名,使用各种 HandlerMethodArgumentResolver 来解析方法的参数(如 @RequestParam, @RequestBody, @PathVariable)。
  2. 调用方法:通过反射调用控制器方法。
  3. 返回值处理:使用方法返回值,使用各种 HandlerMethodReturnValueHandler 处理返回值(如 @ResponseBody, ModelAndView)。
第6步:执行拦截器后置处理 (Interceptors - postHandle)

控制器方法执行完毕后,DispatcherServlet逆序调用所有拦截器的 postHandle() 方法。

  • 应用:有机会修改即将发送到视图的 ModelAndView 对象。
第7步:处理分发结果 (Process Dispatch Result)

这是视图渲染和响应的核心环节。DispatcherServlet 根据控制器方法的返回结果,进行不同的处理:

  • 情况A:方法有 @ResponseBody 或返回 ResponseEntity
    • 流程RequestResponseBodyMethodProcessor 会使用配置的 HttpMessageConverter(如 MappingJackson2HttpMessageConverter)将返回值(如一个 Java 对象)直接序列化(如转为 JSON),并写入 HttpServletResponse 的输出流。此过程不涉及视图解析。
  • 情况B:方法返回视图名(如 String)或 ModelAndView
    • 视图解析DispatcherServlet 调用 ViewResolver 来根据逻辑视图名(如 "success")解析为一个具体的 View 对象(如 InternalResourceView 对应 JSP,ThymeleafView 对应 Thymeleaf)。
    • 视图渲染DispatcherServlet 将模型数据传递给 View 对象,并调用其 render() 方法。该方法会生成最终的 HTML 内容(如合并 JSP 模板和模型数据),并将其写入 HttpServletResponse 的输出流。
第8步:执行拦截器完成处理 (Interceptors - afterCompletion)

无论请求处理成功还是出现异常,DispatcherServlet 都会最终触发所有拦截器的 afterCompletion() 方法。

  • 应用:进行资源清理、记录请求完成时间等。
第9步:返回响应 (HTTP Response)

最终,完整的响应通过 Servlet 容器(如 Tomcat)返回给客户端。


核心组件总结

组件职责类比
DispatcherServlet前端控制器,协调所有组件,是整个流程的总指挥。餐厅的前台经理
HandlerMapping请求映射,根据 URL 找到对应的处理器。餐厅的引座员
HandlerAdapter处理器适配,用统一的接口调用各种不同类型的处理器。餐厅的服务员,连接经理和后厨
Handler处理器(通常是我们写的 @Controller)。餐厅的厨师
ViewResolver视图解析器,将逻辑视图名解析为具体视图对象。餐厅的出菜员
View视图,负责将模型数据渲染成最终的响应内容(HTML/JSON等)。最终呈现的菜肴
HandlerInterceptor拦截器,在请求处理的不同阶段进行横切处理。餐厅的质检员

这个流程充分体现了 Spring MVC 的高度可配置性和可扩展性。其中的每一个步骤几乎都可以通过配置自定义组件或实现特定接口来进行干预和扩展。

http://www.xdnf.cn/news/1473517.html

相关文章:

  • 提示语规则引擎:spring-ai整合liteflow
  • [Upscayl图像增强] 多种AI处理模型 | 内置模型与自定义模型
  • IDEA修改系统缓存路径,防止C盘爆满
  • echarts实现两条折线区域中间有线连接,custom + renderItem(初级版)
  • 本地MOCK
  • Redis中的List数据类型
  • 002 -Dephi -Helloworld
  • 浅谈前端框架
  • Redis-主从复制-哨兵模式
  • 【音视频】H264编码参数优化和cbr、vbr、crf模式设置
  • 在Ubuntu 22.04系统中无需重启设置静态IP地址
  • C++协程理解
  • PCL的C++底层原理
  • 【洛谷】队列相关经典算法题详解:模板队列、机器翻译、海港
  • 【UE】 实现指向性菲涅尔 常用于圆柱体的特殊菲涅尔
  • 分享一种常被忽略的芯片死锁
  • 【Linux基础】Linux系统管理:MBR分区实践详细操作指南
  • IO进程线程;多线程;线程互斥同步;互斥锁;无名信号量;条件变量;0905
  • FEMDRW032G-88A19江波龙,工业级宽温EMMC存储FEMDRW032G采用eMMC5.1协议,具备32GB存储容量提供方案
  • 可搜索且多选的下拉式列表
  • Linux查看设备树信息
  • C++Primerplus 编程练习 第十二章
  • CUDA编程12 - 使用OpenMP控制多个GPU示例
  • 1个工具管好15+网盘(批量转存/分享实测)工具实测:批量转存 + 自动换号 + 资源监控 账号添加失败 / 转存中断?这样解决(含功能详解)
  • 【leetcode】46. 全排列
  • 【C++】vectore
  • 裸机程序(3)
  • 【C++】 priority_queue 容器模拟实现解析
  • GDAL 开发起步
  • MySQL抛出的Public Key Retrieval is not allowed