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/
面试考察点
面试官提出这个问题,主要想考察以下几个层面的理解,而不仅仅是让你背诵一个流程:
- 对 Spring MVC 核心架构和设计模式的理解:你是否理解 Spring MVC 基于 “前端控制器(Front Controller)” 模式,以及其松耦合、可插拔的组件化设计思想。
- 对请求处理生命周期中关键组件的掌握:你能否清晰说出
DispatcherServlet、HandlerMapping、HandlerAdapter、ViewResolver等核心组件在流程中扮演的角色及其协作关系。 - 将理论映射到实践和配置的能力:面试官不仅想知道 “是什么”,更想知道 “如何用”。你是否了解这些组件在 XML 或 Java Config 中如何配置,以及如何通过注解(如
@Controller、@RequestMapping)驱动开发。 - 对异常处理、拦截器等扩展点的认识:一个成熟的开发者需要知道在流程的哪个环节可以插入自定义逻辑(如权限校验、日志记录)或进行统一异常处理。
核心答案
Spring MVC 处理请求的核心流程可以概括为一条清晰的 “请求响应链”,由前端控制器 DispatcherServlet 统一调度。其核心步骤如下图所示:
简单来说,DispatcherServlet 就像一个公司的 “中央调度员”,所有请求都先发到它这里。它自己不处理业务,而是询问 “路由表”(HandlerMapping)该找哪个部门(Controller),再找到能跟这个部门沟通的 “协调员”(HandlerAdapter)去具体执行。执行完成后,将结果交给 “展厅布置员”(ViewResolver)找到合适的展示模板(视图),最终渲染成完整的页面(响应)返回给客户。
深度解析
原理/机制
整个流程建立在 DispatcherServlet 的 doDispatch() 方法之上(FrameworkServlet 类中)。它是一个标准的 Servlet,在 web.xml 或 Servlet 3.0+ 动态注册中配置,并拦截匹配的 URL 模式(通常是 /)。其设计精髓在于职责分离:每个核心组件只做一件事,通过接口抽象,使得替换或扩展某个环节(如更换视图技术)变得非常容易。
核心步骤详解
- 发送请求:用户请求发送至 Web 容器(如 Tomcat),匹配到
DispatcherServlet。 - 映射处理器(Handler Mapping):
DispatcherServlet查询所有HandlerMapping组件,根据请求的 URL、方法等信息,找到对应的 处理器(Handler) 及其关联的拦截器(Interceptor),封装成一个HandlerExecutionChain对象。处理器通常就是我们写的@Controller中的方法。 - 获取适配器(Handler Adapter):
DispatcherServlet遍历HandlerAdapter列表,找到第一个支持当前处理器的适配器。这是适配器模式的典型应用,目的是统一调用各式各样的处理器(比如基于@Controller注解的、实现Controller接口的、HttpRequestHandler等)。 - 执行拦截器前置处理:在真正调用处理器方法前,会按顺序执行
HandlerExecutionChain中所有拦截器的preHandle()方法。若某个拦截器返回false,则流程中断并直接返回。 - 调用处理器(Handle Request):
HandlerAdapter使用反射等技术,调用目标处理器方法。在此过程中,它会负责处理令人称道的参数绑定(将 HTTP 请求参数、头部、Cookie 等绑定到方法参数上)和返回值处理。 - 返回 ModelAndView:处理器方法执行后,会返回一个结果对象。
HandlerAdapter会将其适配包装成一个ModelAndView对象(其中可能包含模型数据Model和逻辑视图名View)。对于 REST API,通常返回@ResponseBody注解的对象,此时视图名为null,流程会跳至第 9 步。 - 执行拦截器后置处理:调用拦截器的
postHandle()方法。 - 处理渲染结果(Process Dispatch Result):
- 解析视图(View Resolution):如果返回的
ModelAndView包含逻辑视图名,DispatcherServlet会调用ViewResolver根据该名称解析出真正的View对象(如 JSP、Thymeleaf、FreeMarker 模板)。 - 渲染视图(Render View):
View对象会结合模型数据(Model)进行渲染,生成最终的响应内容(如 HTML)。
- 解析视图(View Resolution):如果返回的
- 返回响应:将渲染后的内容通过
HttpServletResponse返回给客户端。 - 触发完成通知:最后,无论成功与否,都会触发拦截器的
afterCompletion()方法,进行资源清理等工作。
对比/注意事项
- 与传统 Servlet 开发对比:Spring MVC 通过
DispatcherServlet统一入口,避免了每个 Servlet 都要自己解析参数、跳转视图的重复代码,实现了高度的解耦和可配置性。 Handler与Controller:Handler是一个更广义的概念,指任何能处理请求的端点。我们最常用的@Controller注解标注的类中的方法只是其中一种Handler。- 异步请求处理:从 Spring 3.2 开始,支持基于
Callable或DeferredResult的异步处理。此时,主线程(如 Tomcat 的工作线程)会立即释放,等待异步任务完成后,由另一个线程重新派发请求至DispatcherServlet,走一遍类似的流程(但不再经过拦截器的preHandle),最终渲染返回。
最佳实践
- 明确配置组件扫描路径:确保你的
@Controller能被 Spring 容器发现。@Configuration @EnableWebMvc // 或继承 WebMvcConfigurerAdapter (已过时) / 实现 WebMvcConfigurer @ComponentScan(basePackages = "com.example.controller") public class WebConfig implements WebMvcConfigurer { // 配置视图解析器、静态资源等 } - 合理使用拦截器:将通用的横切关注点(如认证、日志、国际化)放在拦截器中,而不是在每个 Controller 里重复。
- 熟悉常用注解:除了
@RequestMapping,更要掌握@GetMapping、@PostMapping、@RequestParam、@PathVariable、@RequestBody、@ResponseStatus等,它们能极大简化开发。
常见误区
- 认为
DispatcherServlet只有一个:一个应用可以配置多个DispatcherServlet,分管不同的 URL 命名空间,实现模块化。 - 混淆
Model、ModelMap、ModelAndView:它们本质都是用来向视图传递数据的模型容器。在方法参数中注入Model或直接返回Map/ModelAndView是常见用法。 - 忽略异常处理机制:Spring MVC 提供了强大的
@ControllerAdvice和@ExceptionHandler进行全局异常处理,这是流程中处理业务异常的最佳位置,不应让异常直接抛给 Servlet 容器。
总结
Spring MVC 通过 DispatcherServlet 作为中央调度器,协同 HandlerMapping、HandlerAdapter、ViewResolver 等核心组件,以职责分明、高度可配置的流水线方式处理请求,完美体现了 “约定优于配置” 和 “松耦合” 的框架设计哲学。