SpringMVC 处理请求的流程是怎样的?

一则或许对你有用的小广告

欢迎 加入小哈的星球 ,你将获得: 专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新开坑项目: 《Spring AI 项目实战(问答机器人、RAG 增强检索、联网搜索)》 正在持续爆肝中,基于 Spring AI + Spring Boot3.x + JDK 21...点击查看;
  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot3.x + JDK 17...点击查看项目介绍; 演示链接: http://116.62.199.48:7070/;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/

面试考察点

面试官提出这个问题,主要想考察以下几个层面的理解,而不仅仅是让你背诵一个流程:

  1. 对 Spring MVC 核心架构和设计模式的理解:你是否理解 Spring MVC 基于 “前端控制器(Front Controller)” 模式,以及其松耦合、可插拔的组件化设计思想。
  2. 对请求处理生命周期中关键组件的掌握:你能否清晰说出 DispatcherServletHandlerMappingHandlerAdapterViewResolver 等核心组件在流程中扮演的角色及其协作关系。
  3. 将理论映射到实践和配置的能力:面试官不仅想知道 “是什么”,更想知道 “如何用”。你是否了解这些组件在 XML 或 Java Config 中如何配置,以及如何通过注解(如 @Controller@RequestMapping)驱动开发。
  4. 对异常处理、拦截器等扩展点的认识:一个成熟的开发者需要知道在流程的哪个环节可以插入自定义逻辑(如权限校验、日志记录)或进行统一异常处理。

核心答案

Spring MVC 处理请求的核心流程可以概括为一条清晰的 “请求响应链”,由前端控制器 DispatcherServlet 统一调度。其核心步骤如下图所示:

简单来说,DispatcherServlet 就像一个公司的 “中央调度员”,所有请求都先发到它这里。它自己不处理业务,而是询问 “路由表”(HandlerMapping)该找哪个部门(Controller),再找到能跟这个部门沟通的 “协调员”(HandlerAdapter)去具体执行。执行完成后,将结果交给 “展厅布置员”(ViewResolver)找到合适的展示模板(视图),最终渲染成完整的页面(响应)返回给客户。

深度解析

原理/机制

整个流程建立在 DispatcherServletdoDispatch() 方法之上(FrameworkServlet 类中)。它是一个标准的 Servlet,在 web.xml 或 Servlet 3.0+ 动态注册中配置,并拦截匹配的 URL 模式(通常是 /)。其设计精髓在于职责分离:每个核心组件只做一件事,通过接口抽象,使得替换或扩展某个环节(如更换视图技术)变得非常容易。

核心步骤详解

  1. 发送请求:用户请求发送至 Web 容器(如 Tomcat),匹配到 DispatcherServlet
  2. 映射处理器(Handler Mapping)DispatcherServlet 查询所有 HandlerMapping 组件,根据请求的 URL、方法等信息,找到对应的 处理器(Handler) 及其关联的拦截器(Interceptor),封装成一个 HandlerExecutionChain 对象。处理器通常就是我们写的 @Controller 中的方法。
  3. 获取适配器(Handler Adapter)DispatcherServlet 遍历 HandlerAdapter 列表,找到第一个支持当前处理器的适配器。这是适配器模式的典型应用,目的是统一调用各式各样的处理器(比如基于 @Controller 注解的、实现 Controller 接口的、HttpRequestHandler 等)。
  4. 执行拦截器前置处理:在真正调用处理器方法前,会按顺序执行 HandlerExecutionChain 中所有拦截器的 preHandle() 方法。若某个拦截器返回 false,则流程中断并直接返回。
  5. 调用处理器(Handle Request)HandlerAdapter 使用反射等技术,调用目标处理器方法。在此过程中,它会负责处理令人称道的参数绑定(将 HTTP 请求参数、头部、Cookie 等绑定到方法参数上)和返回值处理
  6. 返回 ModelAndView:处理器方法执行后,会返回一个结果对象。HandlerAdapter 会将其适配包装成一个 ModelAndView 对象(其中可能包含模型数据 Model 和逻辑视图名 View)。对于 REST API,通常返回 @ResponseBody 注解的对象,此时视图名为 null,流程会跳至第 9 步。
  7. 执行拦截器后置处理:调用拦截器的 postHandle() 方法。
  8. 处理渲染结果(Process Dispatch Result)
    • 解析视图(View Resolution):如果返回的 ModelAndView 包含逻辑视图名,DispatcherServlet 会调用 ViewResolver 根据该名称解析出真正的 View 对象(如 JSP、Thymeleaf、FreeMarker 模板)。
    • 渲染视图(Render View)View 对象会结合模型数据(Model)进行渲染,生成最终的响应内容(如 HTML)。
  9. 返回响应:将渲染后的内容通过 HttpServletResponse 返回给客户端。
  10. 触发完成通知:最后,无论成功与否,都会触发拦截器的 afterCompletion() 方法,进行资源清理等工作。

对比/注意事项

  • 与传统 Servlet 开发对比:Spring MVC 通过 DispatcherServlet 统一入口,避免了每个 Servlet 都要自己解析参数、跳转视图的重复代码,实现了高度的解耦可配置性
  • HandlerControllerHandler 是一个更广义的概念,指任何能处理请求的端点。我们最常用的 @Controller 注解标注的类中的方法只是其中一种 Handler
  • 异步请求处理:从 Spring 3.2 开始,支持基于 CallableDeferredResult 的异步处理。此时,主线程(如 Tomcat 的工作线程)会立即释放,等待异步任务完成后,由另一个线程重新派发请求至 DispatcherServlet,走一遍类似的流程(但不再经过拦截器的 preHandle),最终渲染返回。

最佳实践

  1. 明确配置组件扫描路径:确保你的 @Controller 能被 Spring 容器发现。
    @Configuration
    @EnableWebMvc // 或继承 WebMvcConfigurerAdapter (已过时) / 实现 WebMvcConfigurer
    @ComponentScan(basePackages = "com.example.controller")
    public class WebConfig implements WebMvcConfigurer {
        // 配置视图解析器、静态资源等
    }
    
  2. 合理使用拦截器:将通用的横切关注点(如认证、日志、国际化)放在拦截器中,而不是在每个 Controller 里重复。
  3. 熟悉常用注解:除了 @RequestMapping,更要掌握 @GetMapping@PostMapping@RequestParam@PathVariable@RequestBody@ResponseStatus 等,它们能极大简化开发。

常见误区

  • 认为 DispatcherServlet 只有一个:一个应用可以配置多个 DispatcherServlet,分管不同的 URL 命名空间,实现模块化。
  • 混淆 ModelModelMapModelAndView:它们本质都是用来向视图传递数据的模型容器。在方法参数中注入 Model 或直接返回 Map/ModelAndView 是常见用法。
  • 忽略异常处理机制:Spring MVC 提供了强大的 @ControllerAdvice@ExceptionHandler 进行全局异常处理,这是流程中处理业务异常的最佳位置,不应让异常直接抛给 Servlet 容器。

总结

Spring MVC 通过 DispatcherServlet 作为中央调度器,协同 HandlerMappingHandlerAdapterViewResolver 等核心组件,以职责分明、高度可配置的流水线方式处理请求,完美体现了 “约定优于配置” 和 “松耦合” 的框架设计哲学。