Spring 源码系列(16): doDispatch 全流程——一次请求的主干链路

📌 阅读前提示 :第 15 篇搭好了 MVC 的组织架构(九大组件)。本篇把镜头拉到「运行时」------每一次 HTTP 请求进来,DispatcherServlet.doDispatch 是如何调度这九个组件的。这是 MVC 源码里最核心、面试命中率最高的一篇,建议配合本篇配图逐行跟。


一、引子:所有请求都收敛到这一个方法

无论你写多少个 Controller,最终它们都通过一个统一的入口被调用------doDispatch。理解它,你就理解了 MVC 的「调度本质」:

找处理器(HandlerMapping)→ 选适配器(HandlerAdapter)→ 执行(拦截器 + 反射调用)→ 渲染(ViewResolver)→ 异常兜底。


二、doDispatch 主干源码

java 复制代码
// DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    try {
        ModelAndView mv = null;
        Exception dispatchException = null;
        try {
            // ① 文件上传请求预处理
            processedRequest = checkMultipart(request);
            // ② 找 Handler(遍历所有 HandlerMapping)
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);   // 404
                return;
            }
            // ③ 找 HandlerAdapter(遍历所有 HandlerAdapter)
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
            // ④ 处理 Last-Modified(GET 缓存)
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;   // 任一 preHandle 返回 false → 中断
            }
            // ⑤ 真正执行 Handler(适配器内部反射调用 Controller 方法)
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            // ⑥ 视图名翻译(无显式视图名时用 RequestToViewNameTranslator)
            applyDefaultViewName(processedRequest, mv);
            // ⑦ 拦截器 postHandle
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        } catch (Exception ex) {
            dispatchException = ex;
        } catch (Throwable err) {
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }
        // ⑧ 处理结果:渲染视图 或 处理异常
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    } finally {
        // ⑨ 清理文件上传资源
        if (multipartRequestParsed) cleanupMultipart(processedRequest);
    }
}

📌 关键顺序preHandlehandle(Controller)→ postHandleprocessDispatchResult(渲染/异常)。注意 postHandlehandle 之后、但在视图渲染之前。


三、三个核心子方法

3.1 getHandler:遍历 HandlerMapping 找处理器

java 复制代码
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        for (HandlerMapping mapping : this.handlerMappings) {
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) return handler;  // 第一个匹配到的返回
        }
    }
    return null;
}

💡 返回的不只是 Handler 方法 :而是 HandlerExecutionChain------它把「目标 Handler + 匹配的拦截器链」打包在一起。@RequestMappingRequestMappingHandlerMapping 处理。

3.2 getHandlerAdapter:按 supports 选适配器

java 复制代码
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
        for (HandlerAdapter adapter : this.handlerAdapters) {
            if (adapter.supports(handler)) return adapter;  // HandlerAdapter.supports 判定
        }
    }
    throw new ServletException("No adapter for handler [" + handler + "]");
}

常见适配器:RequestMappingHandlerAdapter(注解 Controller)、HttpRequestHandlerAdapterSimpleControllerHandlerAdapter(实现 Controller 接口的老式)。

3.3 processDispatchResult:渲染或异常

java 复制代码
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
        @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
        @Nullable Exception exception) throws Exception {
    boolean errorView = false;
    if (exception != null) {
        // 有异常 → 交给 HandlerExceptionResolver 解析
        if (exception instanceof ModelAndViewDefiningException) {
            mv = ((ModelAndViewDefiningException) exception).getModelAndView();
        } else {
            Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
            mv = processHandlerException(request, response, handler, exception); // 异常解析器
            errorView = (mv != null);
        }
    }
    if (mv != null && !mv.wasCleared()) {
        render(mv, request, response);   // ★ 视图渲染
        if (errorView) WebUtils.clearErrorRequestAttributes(request);
    }
    // 触发 afterCompletion(finally 语义)
    if (mappedHandler != null) mappedHandler.triggerAfterCompletion(request, response, null);
}

四、doDispatch 主干流程图


五、常见误区

误区 正解
HandlerMapping 直接调用 Controller 不,它只返回 HandlerExecutionChain,执行交给 Adapter
postHandle 在 Controller 之后立即渲染前执行 是,但异常处理时 postHandle 不执行(异常在 handle 阶段抛出)
拦截器一定有 afterCompletion 只有 preHandle 返回 true 的拦截器,才会触发 afterCompletion
404 也会走 processDispatchResult 不,getHandler 返回 null 时直接 noHandlerFound 返回
视图渲染由 ViewResolver 完成 ViewResolver 只「解析出 View 对象」,渲染由 View.render() 负责

🧪 面试题自测

  1. doDispatch 的完整执行顺序?
  2. getHandler 返回的是什么对象?为什么是「链」?
  3. HandlerAdapter 的 supports 起什么作用?
  4. 拦截器 preHandle / postHandle / afterCompletion 的触发时机与条件?
  5. 异常发生时,postHandle 还会执行吗?为什么?
  6. 404 和 500 在 doDispatch 里的处理路径有何不同?

🔧 Debug 小技巧

doDispatch 第一行打断点,用浏览器访问一个接口,单步跟 getHandler → getHandlerAdapter → applyPreHandle → ha.handle → applyPostHandle → processDispatchResult,观察 mappedHandler 里拦截器数量、mv 里的视图名。这是理解 MVC 运行时最直观的方式。


下一篇预告

第 17 篇:深入 HandlerMappingHandlerAdapter 两大体系------@RequestMapping 是怎么被注册成映射的?适配器内部又如何反射调用你的 Controller 方法?


如果这篇对你有帮助,欢迎 点赞 · 收藏 · 关注 三连支持。

Spring 源码系列共 30 篇,由浅入深持续更新中。有疑问或想深挖的源码点,评论区告诉我,下篇见。

相关推荐
nvvas9 分钟前
Spring AI 2.0正式GA:Token砍掉64%,Java的AI反击战打响了
java·人工智能
程序员阿鹏26 分钟前
双亲委派机制
java·jvm·数据结构·后端
65岁退休Coder36 分钟前
把 Agent 框架拆开:PI 开发生产级 Harness
后端·node.js·agent
步行cgn42 分钟前
Spring Bean 的生命周期详解
java·后端·spring
ps酷教程1 小时前
数据权限DataPersmission
java·学习
驭渊的小故事1 小时前
Java 项目-jckson序列化工具
java·开发语言
君顾11 小时前
上海24小时自助健身房系统开发实战指南:从架构设计到功能实现
java·开发语言·健身房
爱勇宝1 小时前
用了一个月 WorkBuddy,聊聊我的真实感受
前端·后端·程序员
用户3721574261351 小时前
Java 拆分 Word 文档教程:按页、分页符和分节符拆分
java