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 篇,由浅入深持续更新中。有疑问或想深挖的源码点,评论区告诉我,下篇见。

相关推荐
IT_陈寒1 小时前
SpringBoot自动配置失效?这个隐藏配置坑了我一整晚
前端·人工智能·后端
搜狐技术产品小编20231 小时前
告别“黑盒”与误判:如何用“多智能体对抗辩论”重构内容安全审核系统
后端·多agent
snow@li1 小时前
HikariCP:高性能数据库连接池全景深入梳理
java·数据库
DantyWei2 小时前
controller注册及调用时机
后端
edwarddamon2 小时前
Spring Cloud 配置热更新与 Bean 代理机制梳理
java·后端
码事漫谈2 小时前
我用 Seed Evolving 做了个 AI 小说写作工具
后端
Raas1002 小时前
MAIGateway,魔芋企业级AI网关的FinAPI成本竞争力设计
java·后端·网关·api网关·魔芋ai·finapi·mai gateway
evans在进步3 小时前
Spring AI 从入门到实战:用 Java 实现大模型对话与 Tool Calling
java·人工智能·spring
evans在进步3 小时前
LeetCode 189 轮转数组:三次反转原地解决,图解 Java 实现
java·算法·leetcode