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


面试考察点

  1. 核心组件认知:面试官不仅仅是想知道流程顺序,更想知道你是否清楚 DispatcherServletHandlerMappingHandlerAdapterViewResolver 等核心组件各自的职责。
  2. 源码级理解:能否说出 DispatcherServletdoDispatch() 方法中的关键调用链路,而不是只停留在表面背诵。
  3. 扩展点掌握:是否了解 拦截器Interceptor)和 过滤器Filter)在请求处理流程中的位置,以及如何利用它们做定制化处理。

核心答案

SpringMVC 处理请求的核心是 DispatcherServlet(前端控制器),它是一个标准的 "模板方法" 模式。整个流程可以概括为 9 个步骤

步骤组件做了什么
1用户发送请求到 DispatcherServlet
2HandlerMapping根据 URL 找到对应的 Handler(Controller 方法)
3DispatcherServlet拿到 Handler 对应的 HandlerAdapter
4HandlerAdapter执行拦截器的 preHandle()
5HandlerAdapter执行 Handler(Controller 方法),返回 ModelAndView
6DispatcherServlet执行拦截器的 postHandle()
7ViewResolver如果有视图,解析视图名称为具体 View
8View渲染视图(模型数据填充到视图)
9DispatcherServlet执行拦截器的 afterCompletion(),返回响应

前后端分离项目(REST API)中,步骤 7 和 8 通常跳过@ResponseBody 直接将返回值序列化为 JSON 写回客户端。

深度解析

一、完整请求处理流程图

上图展示了 SpringMVC 处理一个 HTTP 请求的完整生命周期。按阶段拆解:

  • 阶段一(请求分发):所有请求统一由 DispatcherServlet 接收。它是整个流程的 "总调度",请求来了之后,它不自己干活,而是委派给各个组件去处理。
  • 阶段二(查找 Handler)DispatcherServlet 调用 HandlerMapping,根据请求的 URL、HTTP Method 等信息,找到对应的 Controller 方法。返回的是一个 HandlerExecutionChain,里面包含了目标 Handler 和配置在该 Handler 上的拦截器链。
  • 阶段三(适配执行):拿到 Handler 后,DispatcherServlet 会再找一个 HandlerAdapter 来执行它。为什么要多一层适配器?因为 Handler 可能有不同的类型(@RequestMapping 方法、Controller 接口实现类、HttpRequestHandler 等),适配器负责统一调用方式。
  • 阶段四(拦截器前置处理):执行所有拦截器的 preHandle() 方法。如果任何一个返回 false,请求直接中断,跳到 afterCompletion()
  • 阶段五(执行业务逻辑)HandlerAdapter 通过反射调用 Controller 方法,执行业务逻辑,处理参数绑定、数据校验等。返回值可能是 ModelAndView,也可能是直接的对象(@ResponseBody 场景)。
  • 阶段六(拦截器后置处理):执行所有拦截器的 postHandle() 方法,可以对 ModelAndView 做进一步修改。
  • 阶段七(视图解析或 JSON 序列化):如果是传统 MVC 返回视图名,ViewResolver 把逻辑视图名解析为具体的 View 对象;如果是 @ResponseBodyHttpMessageConverter 直接把返回对象序列化为 JSON。
  • 阶段八(视图渲染)View 对象把 Model 数据填充到模板中,生成最终的 HTML。
  • 阶段九(最终回调):执行拦截器的 afterCompletion(),用于资源清理、日志记录等。无论请求成功还是异常,这个方法都会执行。

二、核心组件详解

面试的时候不能光背流程,面试官一定会追问各个组件是干什么的:

组件职责常见实现
DispatcherServlet前端控制器,统一接收和分发请求Spring 内置
HandlerMapping根据请求 URL 找到对应的 HandlerRequestMappingHandlerMapping
HandlerAdapter适配并执行不同类型的 HandlerRequestMappingHandlerAdapter
Handler具体的业务处理器你写的 @RequestMapping 方法
HandlerInterceptor拦截器,在 Handler 执行前后做拦截处理自定义实现 HandlerInterceptor
ViewResolver视图解析器,把视图名解析为 View 对象InternalResourceViewResolver
View视图,负责渲染最终页面JstlViewThymeleafView
HttpMessageConverter消息转换器,负责请求/响应的序列化与反序列化MappingJackson2HttpMessageConverter

三、源码层面的 doDispatch()

整个流程的源码入口在 DispatcherServlet.doDispatch(),核心逻辑就这几行(简化版):

// DispatcherServlet.java(简化版源码)
protected void doDispatch(HttpServletRequest request,
                          HttpServletResponse response) throws Exception {

    // 1. 根据 request 找到 Handler(包含拦截器链)
    HandlerExecutionChain mappedHandler =
        getHandler(request);

    // 2. 找到对应的 HandlerAdapter
    HandlerAdapter handlerAdapter =
        getHandlerAdapter(mappedHandler.getHandler());

    // 3. 执行拦截器 preHandle()
    if (!mappedHandler.applyPreHandle(request, response)) {
        return;  // 返回 false,中断请求
    }

    // 4. 执行 Handler(Controller 方法)
    ModelAndView mv = handlerAdapter.handle(request, response,
        mappedHandler.getHandler());

    // 5. 执行拦截器 postHandle()
    mappedHandler.applyPostHandle(request, response, mv);

    // 6. 处理视图渲染 / JSON 序列化
    processDispatchResult(request, response, mappedHandler, mv);
}

源码其实很清晰,就是一条线:找 Handler → 找适配器 → 拦截器前置 → 执行业务 → 拦截器后置 → 处理结果。

四、前后端分离 vs 传统 MVC

现在大部分项目都是前后端分离的,这个流程会简单很多:

// 传统 MVC:返回视图名,需要 ViewResolver 解析
@GetMapping("/user")
public String getUser(Model model) {
    model.addAttribute("user", userService.getById(1L));
    return "userDetail";  // → ViewResolver → userDetail.html
}

// 前后端分离:直接返回 JSON,跳过 ViewResolver 和 View 渲染
@GetMapping("/user")
@ResponseBody  // 或者类上用 @RestController
public User getUser() {
    return userService.getById(1L);
    // → HttpMessageConverter → JSON 写回响应
}

前后端分离场景下,@ResponseBody(或 @RestController)会让流程在步骤 5 之后直接走 HttpMessageConverter,把返回对象序列化为 JSON 写回客户端,跳过 ViewResolverView 渲染这两个步骤。

五、拦截器 vs 过滤器

面试的时候经常会被问到拦截器和过滤器的区别,因为它们都在请求处理流程中起作用:

请求 → Filter① → Filter② → DispatcherServlet → Interceptor① → Interceptor② → Handler
响应 ← Filter① ← Filter② ← DispatcherServlet ← Interceptor① ← Interceptor② ← Handler
对比维度Filter(过滤器)Interceptor(拦截器)
规范Servlet 规范Spring MVC 规范
作用范围所有请求(包括静态资源)只针对 DispatcherServlet 处理的请求
执行时机DispatcherServlet 之前Handler 执行前后
获取 Bean不方便(Servlet 容器管理)可以直接 @Autowired 注入 Spring Bean
典型场景编码设置、跨域处理、XSS 过滤登录校验、权限控制、日志记录

选择建议:需要用到 Spring 容器中的 Bean 的场景用拦截器;纯粹的 HTTP 层面的处理用过滤器。大部分业务场景用拦截器就够了。

面试高频追问

  1. 追问一:DispatcherServlet 是线程安全的吗?

    • DispatcherServlet 本身是单例的,但它是无状态的(不会在成员变量中保存请求数据),所以是线程安全的。每个请求通过方法参数(HttpServletRequestHttpServletResponse)传递数据,不共享状态。
  2. 追问二:Spring MVC 如何处理异常?

    • DispatcherServletdoDispatch() 中有 try-catch,捕获到异常后会交给 HandlerExceptionResolver 处理。可以自定义 @ControllerAdvice + @ExceptionHandler 来统一处理异常,返回自定义的错误响应。
  3. 追问三:如何实现一个登录拦截器?

    • 实现 HandlerInterceptor 接口,在 preHandle() 中检查 session 或 token,未登录则重定向或返回 401。然后在 WebMvcConfigurer 中注册拦截器并配置拦截路径。

常见面试变体

  • "说一下 DispatcherServlet 的工作流程?"
  • "Spring MVC 的九大组件是什么?"
  • "HandlerMappingHandlerAdapter 的作用是什么?"
  • "Spring MVC 拦截器和过滤器有什么区别?"

记忆口诀

收 → 找 → 配 → 拦 → 执 → 拦 → 解 → 渲 → 回

翻译:接收请求 → 找 Handler → 配适配器 → 拦截器前置 → 执行 Handler → 拦截器后置 → 解析视图 → 渲染视图 → 最终回调

前后端分离项目记住一句话:走到第 5 步直接 JSON 序列化返回,后面两个拦的(ViewResolverView)都跳过

总结

SpringMVC 的请求处理流程本质就是 DispatcherServlet 的模板方法模式:统一接收请求 → HandlerMapping 找处理器 → HandlerAdapter 执行处理器 → 拦截器链前后拦截 → 处理结果(视图渲染或 JSON 序列化)。面试时把这条主线讲清楚,再配上拦截器和过滤器的区别,基本就能拿满分。