人人都会AI编程

12.1 MVC 架构与 DispatcherServlet 请求处理全流程

更新时间:2026-07-11

Spring MVC 是建立在 Servlet API 之上的请求驱动型 Web 框架,其核心设计围绕 前端控制器模式(Front Controller) 展开。所有进入应用的 HTTP 请求都由一个统一的入口——DispatcherServlet 接管,它负责协调各个专用组件完成请求解析、处理器调用、视图渲染等步骤。理解整个处理流程,是掌握 Spring MVC 开发与排错的关键。

12.1.1 Spring MVC 架构概览

在整个 MVC 体系中,组件各司其职,DispatcherServlet 作为总调度,既不亲自处理业务,也不直接生成响应,而是委托给一系列接口完成。典型架构如图所示:

HTTP 请求 → DispatcherServlet → HandlerMapping → Controller (Handler)
                                      ↓
                               HandlerAdapter
                                      ↓
                               拦截器 pre/post
                                      ↓
                               ViewResolver ← ModelAndView
                                      ↓
                                 View 渲染
                                      ↓
                                HTTP 响应
  • HandlerMapping:根据请求 URL、方法、参数等信息匹配具体的处理器(Handler),通常是控制器中的方法。
  • HandlerAdapter:实际调用处理器,因为处理器可以多种多样(@ControllerHttpRequestHandler、Servlet 等),适配器统一了调用方式。
  • HandlerInterceptor:拦截器链,可以在处理器前后执行通用逻辑(如登录检查、日志记录)。
  • ViewResolver:根据逻辑视图名解析为具体的视图技术(JSP、Thymeleaf、JSON 等)。
  • View:负责将模型数据渲染到响应中。

这些组件通过 IoC 容器装配在一起,Spring Boot 的自动配置会提供一套默认实现,开发时只需关注控制器本身。

12.1.2 DispatcherServlet 的初始化与核心方法

DispatcherServlet 本质上是一个 Servlet,在初始化阶段 (init()) 会完成 Spring 上下文的加载以及策略组件的初始化。其处理入口为 service() 方法,最终流转到 doDispatch()

doDispatch() 是所有请求处理的核心,代码精简后的关键步骤如下:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // 1. 检查是否为文件上传请求,如果是则封装为 MultipartHttpServletRequest
    HttpServletRequest processedRequest = checkMultipart(request);
    
    // 2. 根据请求查找 HandlerExecutionChain(处理器 + 拦截器链)
    HandlerExecutionChain mappedHandler = getHandler(processedRequest);
    if (mappedHandler == null) {
        noHandlerFound(processedRequest, response);
        return;
    }
    
    // 3. 为当前 Handler 查找对应的 HandlerAdapter
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
    // 4. 执行拦截器的 preHandle()
    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
        return; // 拦截器返回 false,请求终止
    }
    
    // 5. 通过适配器真正调用处理器,返回 ModelAndView
    ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
    // 6. 如果存在默认视图名则应用
    applyDefaultViewName(processedRequest, mv);
    
    // 7. 执行拦截器的 postHandle()
    mappedHandler.applyPostHandle(processedRequest, response, mv);
    
    // 8. 处理结果(解析视图、渲染)
    processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}

这个流程清晰地揭示了请求从进入到离开的完整生命周期,下面逐一展开各组件的工作细节。

12.1.3 HandlerMapping:请求匹配到处理器

HandlerMapping 的核心任务是返回一个 HandlerExecutionChain 对象,它包含:

  • 处理器本身(Handler),例如我们定义的 @Controller 中的某个方法。
  • 适用于该请求的所有拦截器(HandlerInterceptor)。

Spring MVC 内置了多种 HandlerMapping 实现:

  • RequestMappingHandlerMapping:最常用的一种,处理 @Controller@RequestMapping 注解。它在容器启动时扫描所有 Bean 中的 @RequestMapping 注解,建立请求路径到处理方法的映射关系。
  • BeanNameUrlHandlerMapping:将 URL 与 Bean 的名称直接绑定,例如 /hello 对应名为 /hello 的 Bean。
  • SimpleUrlHandlerMapping:允许通过 XML 或 Properties 明确指定 URL 到处理器的映射,常用于静态资源或老式配置。

RequestMappingHandlerMapping 为例,当请求 /orders/123 到达时,它会根据 URL 模式、请求方法(GET/POST)、参数条件等匹配到 OrderController.getOrder(@PathVariable Long id) 方法。

如果有多个 HandlerMapping 共存,DispatcherServlet 会按顺序遍历,返回第一个非空的匹配结果。Spring Boot 默认仅注册 RequestMappingHandlerMapping,足以覆盖绝大多数场景。

12.1.4 HandlerAdapter:适配多种处理器类型

找到处理器之后,需要使用合适的适配器去执行。为什么需要适配器?因为处理器的形态并不统一:

  • @Controller 注解类的方法,需要参数解析、返回值处理(@ResponseBody 等)。
  • 实现了 HttpRequestHandler 接口的 Bean,直接操作 HttpServletRequest/HttpServletResponse
  • 实现了 Servlet 接口的 Bean(较少用)。

HandlerAdapter 接口定义了三个核心方法:

  • supports(Object handler):判断是否支持该处理器。
  • handle(request, response, handler):实际调用处理器。
  • getLastModified(request, handler):处理 Last-Modified 头,支持缓存控制。

最核心的适配器是 RequestMappingHandlerAdapter,它与 RequestMappingHandlerMapping 配套,专门处理 @RequestMapping 方法。在处理时会完成一系列关键操作:

  • 参数解析:通过 HandlerMethodArgumentResolver 解析控制器方法参数,例如将 @RequestParam String name 绑定到请求参数、将 @RequestBody Order order 解析为 JSON 对象、将 HttpSession 直接注入等。
  • 数据绑定与校验:若参数标注了 @Valid@Validated,触发 JSR-303 校验。
  • 返回值处理:通过 HandlerMethodReturnValueHandler 处理不同的返回值,如返回字符串代表视图名、@ResponseBody 转为 JSON、ResponseEntity 可自定义头与状态码、Callable/DeferredResult 实现异步处理。

这种适配器模式使得扩展新的处理器类型变得非常容易,也无需改动 DispatcherServlet 主体流程。

12.1.5 HandlerInterceptor:拦截器链

HandlerInterceptor 提供了三个钩子方法,可以在处理器前后植入横切逻辑:

  • preHandle(request, response, handler):处理器执行前调用,返回 false 则终止整个请求,常用于权限校验、登录检查。
  • postHandle(request, response, handler, modelAndView):处理器执行后、视图渲染前调用,可对 ModelAndView 进行修改(如追加公共数据)。
  • afterCompletion(request, response, handler, ex):请求完成(视图渲染结束)后调用,适合资源清理、日志记录,即使出现异常也会执行。

doDispatch 流程中,applyPreHandle 会依次执行所有拦截器的 preHandle;若任一返回 false,则后续拦截器不再执行,直接结束请求。业务方法正常返回后,applyPostHandle 逆序执行 postHandle。最终在 afterCompletion 阶段,无论成功或异常,都会执行对应的清理逻辑。

使用拦截器而不是直接在控制器中写通用逻辑,可以保持代码整洁和可复用:

@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (request.getSession().getAttribute("user") == null) {
            response.setStatus(401);
            return false;
        }
        return true;
    }
}

然后在配置类中注册拦截器并指定拦截路径:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/login", "/public/**");
    }
}

12.1.6 处理结果与异常解析

控制器返回的 ModelAndView 并非直接渲染,而是要经过视图解析。ViewResolver 根据逻辑视图名(如 "orderDetail")匹配真实视图。常见的 ViewResolver 有:

  • InternalResourceViewResolver:针对 JSP,将逻辑名转换为 /WEB-INF/views/ 下的 .jsp 路径。
  • ThymeleafViewResolver:Thymeleaf 模板引擎,解析逻辑名为 Thymeleaf 模板文件。
  • BeanNameViewResolver:将视图名作为 Bean 名称查找。
  • ContentNegotiatingViewResolver:根据请求的 Accept 头或文件扩展名选择最合适的视图(如 HTML、JSON、XML)。

对于 REST 接口,场景稍有不同。通常使用 @ResponseBody@RestController,此时处理器返回的不是视图名,而是直接由 HttpMessageConverter 将返回值序列化为 JSON/XML 写入响应体。在这种情况下,ModelAndViewnullprocessDispatchResult 直接认为请求已完成。

如果在处理过程中抛出异常,DispatcherServlet 会遍历注册的 HandlerExceptionResolver

  • ExceptionHandlerExceptionResolver:处理 @ExceptionHandler 注解的方法。
  • ResponseStatusExceptionResolver:处理 @ResponseStatus 注解的异常,返回对应 HTTP 状态码。
  • DefaultHandlerExceptionResolver:处理 Spring MVC 内部异常,转换为标准状态码。

开发者最常用的是在控制器内或 @ControllerAdvice 全局类中定义 @ExceptionHandler 方法,统一处理特定异常并返回友好的错误响应。

12.1.7 带着流程图走一遍真实请求

用一个具体场景串联整个流程:用户请求 GET /orders/1001,期望返回该订单的 JSON 数据。

  1. 请求到达 DispatcherServlet,触发 doDispatch
  2. RequestMappingHandlerMapping 匹配到 OrderController.getOrder(Long id) 方法,返回 HandlerExecutionChain(含处理器和拦截器列表)。
  3. 执行拦截器 preHandle,例如校验 Token,通过后继续。
  4. 通过 HandlerMethodArgumentResolver 解析 {id} 路径变量,将 1001 注入参数。
  5. 调用 OrderController.getOrder(1001) 方法,内部从数据库查询订单,转为订单 DTO 返回。
  6. 因为方法标注了 @ResponseBodyHandlerMethodReturnValueHandler 使用 MappingJackson2HttpMessageConverter 将 DTO 序列化为 JSON,写入响应体。
  7. 拦截器 postHandle 执行(此时 ModelAndView 为 null)。
  8. processDispatchResult 检测到 ModelAndView 为空,认为处理已完成。
  9. 拦截器 afterCompletion 执行,完成日志记录。
  10. 浏览器收到 200 状态码与 JSON 响应。

整个流程分工明确,每个组件只关注自身的职责,这正是 Spring MVC 高扩展性的基础。在实际开发中,掌握这个处理链对于调试请求映射失败、拦截器失效、异常处理不生效等问题至关重要。