一个请求在Spring MVC 中是怎么流转的

💡 核心入口:DispatcherServlet

所有Spring MVC请求的入口都是DispatcherServlet。它本质上是一个Servlet,其service()方法(由父类FrameworkServlet实现)最终会导向其核心方法doDispatch()。这个方法就像一个指挥中心,调度其他组件来完成请求处理。

java 复制代码
// FrameworkServlet.java
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {
    processRequest(request, response);
}
// ... doPost, doPut, doDelete等方法类似
// processRequest方法最终会调用到DispatcherServlet的doService()方法
java 复制代码
// DispatcherServlet.java
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) 
        throws Exception {
    // ... 做一些准备工作和属性设置 ...
    doDispatch(request, response); // 核心分发逻辑
}

🚦 请求全链路剖析:doDispatch() 源码逐段解析

下面的流程图清晰地展示了doDispatch内部的核心步骤,我将配合每个步骤的源码进行深入解读。
"如果返回false则中断"
"如果返回true则继续"
客户端发起请求
DispatcherServlet接收请求
checkMultipart

检查是否为文件上传请求
getHandler

通过HandlerMapping获取处理器执行链
getHandlerAdapter

获取支持该处理器的适配器
applyPreHandle

执行拦截器的preHandle方法
响应返回
ha.handle

适配器执行处理器方法
applyPostHandle

执行拦截器的postHandle方法
processDispatchResult

处理结果及异常
render

视图渲染
triggerAfterCompletion

执行拦截器的afterCompletion方法

  1. checkMultipart:处理文件上传

    流程开始,会检查当前请求是否是一个multipart/form-data类型的文件上传请求。如果是,则通过配置的MultipartResolverHttpServletRequest对象包装为MultipartHttpServletRequest,以便后续方便地获取上传的文件。

  2. getHandler:定位处理器

    接下来,系统需要找到"谁来处理这个请求"。getHandler(request)方法会遍历注册的HandlerMapping组件,找到能处理当前请求的处理器(Handler),并返回一个包含该处理器和其关联的HandlerInterceptor(拦截器)的执行链。

    java 复制代码
    // DispatcherServlet.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;
    }

    最常用的HandlerMappingRequestMappingHandlerMapping,它在项目启动时,会扫描所有标注了@Controller@RequestMapping的类和方法,将请求路径(和HTTP方法)与目标方法(封装为HandlerMethod对象)的映射关系保存起来。getHandler方法的核心逻辑就在AbstractHandlerMethodMapping.lookupHandlerMethod中,它会根据请求路径在内部的mappingRegistry中进行精准匹配。

  3. getHandlerAdapter:查找适配器

    找到处理器后,DispatcherServlet需要一种统一的方式来调用它,因为处理器的形式可能是多样的(如实现Controller接口的类,或是标注了@RequestMapping的方法)。getHandlerAdapter方法的作用就是遍历所有注册的HandlerAdapter,找到能"支持"当前处理器的那个适配器,并在后续操作中使用它。

    java 复制代码
    // DispatcherServlet.java
    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            // 遍历所有注册的处理器适配器
            for (HandlerAdapter adapter : this.handlerAdapters) {
                if (adapter.supports(handler)) { // 判断是否支持该处理器
                    return adapter; // 返回第一个找到的适配器
                }
            }
        }
        throw new ServletException("No adapter for handler...");
    }

    对于@RequestMapping注解的方法,Spring MVC会为其选择RequestMappingHandlerAdapter。这个适配器是处理现代Spring MVC控制器(以@Controller@RequestMapping为代表)的核心。

  4. 拦截器「前置处理」:applyPreHandle

    在真正调用处理器方法之前,applyPreHandle方法会按照顺序执行处理器执行链中所有拦截器的preHandle方法。

    如果任何一个拦截器的preHandle方法返回false,整个请求链路将在此被切断,后续的处理器方法和视图渲染都不会执行。

  5. ha.handle:执行处理器

    这是核心的业务处理阶段。HandlerAdapterhandle方法会利用Java的反射机制调用真正的Controller方法 。在这个过程中,HandlerAdapter还会负责处理诸多繁重的工作,例如:

    • 参数解析:将请求参数(Query String、Form Data、JSON等)绑定到方法参数上。
    • 返回值处理 :将方法返回的对象(如ModelAndView、POJO、ResponseEntity等)转换为统一的ModelAndView对象。
    java 复制代码
    // RequestMappingHandlerAdapter.java (精简逻辑)
    @Override
    protected ModelAndView handleInternal(HttpServletRequest request,
                                          HttpServletResponse response,
                                          HandlerMethod handlerMethod) throws Exception {
        ModelAndView mav = null;
        // ...
        // 最终会委托给 ServletInvocableHandlerMethod 来调用 Controller 方法
        ServletInvocableHandlerMethod invocableMethod = ...;
        invocableMethod.invokeAndHandle(request, response, mavContainer);
        // ...
        return getModelAndView(...);
    }
  6. 拦截器「后置处理」:applyPostHandle

    在处理器方法执行完毕、但视图尚未渲染之前,applyPostHandle方法会按逆序 执行所有拦截器的postHandle方法。

    如果处理器方法执行过程中抛出了异常,postHandle方法将不会被执行。

  7. processDispatchResult:处理结果

    这个方法负责处理执行结果,主要包含两个子任务:

    • 异常处理 :如果处理器执行过程中抛出了异常,它会调用配置的HandlerExceptionResolver组件来解析异常,生成一个包含错误视图的ModelAndView
    • 视图渲染 :调用render方法,渲染最终的响应结果。
    java 复制代码
    // DispatcherServlet.java
    private void processDispatchResult(HttpServletRequest request, 
                                       HttpServletResponse response,
                                       @Nullable HandlerExecutionChain mappedHandler, 
                                       @Nullable ModelAndView mv,
                                       @Nullable Exception exception) throws Exception {
        // 1. 处理异常
        if (exception != null) {
            // 调用 HandlerExceptionResolver 来解析异常,生成对应的 ModelAndView
            mv = processHandlerException(request, response, mappedHandler, exception);
        }
        // 2. 视图渲染
        if (mv != null && !mv.wasCleared()) {
            render(mv, request, response);
        }
        // ... 清理和后续处理
    }
  8. render:视图渲染

    如果ModelAndView包含视图信息(非@ResponseBody),该方法会执行:

    • 解析视图 :遍历所有ViewResolver,调用其resolveViewName方法,将逻辑视图名(如"user/list")解析为一个具体的View对象(如InternalResourceView,对应/WEB-INF/jsp/userList.jsp)。
    • 渲染输出 :调用View对象的render方法,将模型数据(Model)填充到视图中,生成最终的HTML(或其他格式)内容,并写入HttpServletResponse
    java 复制代码
    // DispatcherServlet.java
    protected void render(ModelAndView mv, HttpServletRequest request, 
                          HttpServletResponse response) throws Exception {
        // 解析视图名称
        View view = null;
        if (mv.isReference()) {
            // 遍历 ViewResolver 来解析视图名称
            view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
        }
        // 渲染视图
        view.render(mv.getModelInternal(), request, response);
    }
  9. 拦截器「完成处理」:triggerAfterCompletion

    无论请求是正常处理完成,还是中途发生了异常,triggerAfterCompletion方法都会在最后被调用,按逆序 执行所有拦截器的afterCompletion方法,用于执行清理资源等收尾工作。


🔗 总览:请求流转全路径总结图

View ViewResolver HandlerExceptionResolver Controller HandlerAdapter HandlerInterceptor HandlerMapping DispatcherServlet 客户端 View ViewResolver HandlerExceptionResolver Controller HandlerAdapter HandlerInterceptor HandlerMapping DispatcherServlet 客户端 alt 存在异常 alt preHandle返回true preHandle返回false 发送请求 checkMultipart (文件上传检查) getHandler (定位处理器) 返回 HandlerExecutionChain getHandlerAdapter (获取适配器) applyPreHandle (前置拦截) handle (执行处理器) 返回ModelAndView applyPostHandle (后置拦截) processDispatchResult (处理结果) processHandlerException (解析异常) 返回错误 ModelAndView resolveViewName (解析视图) 返回View对象 render (视图渲染) 返回响应 直接返回 (请求中断) triggerAfterCompletion (完成清理)

相关推荐
SL_staff1 小时前
别急着买商业规则引擎:90%风控场景根本不需要全量编码能力
java·程序员·groovy
ManageEngineITSM1 小时前
DevOps和ITIL是什么关系?是替代还是互补一文讲清
java·服务器·资产管理·变更管理
SL_staff2 小时前
ERP排程总在纸上谈兵?JVS-APS如何用真实产能约束打通计划与执行闭环
java·人工智能·开源
xcl09252 小时前
24小时自助健身房系统软件开发实战指南:从架构到部署
java·spring boot
码少女3 小时前
Linux--多路转接之select
java·服务器·数据库
nvvas4 小时前
Java 入门(四):数组——数据的集合管理
java·开发语言
Lambert2814 小时前
AgentScope Java 从零(07):官方有权限引擎,我却在工具里写了个 if
java·后端·ai编程
Kyrie_kk4 小时前
Java--ThreadLocal线程副本(一)
java·后端
君顾15 小时前
本地城市低空经济管理系统定制:城市级低空运营平台架构与落地实战
java·开发语言·飞手
奈斯先生Vector5 小时前
当模型版本不断变化,RelayRouter 能否帮助 AI 应用摆脱深度绑定
android·java·人工智能·开源·aigc