一个请求在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 (完成清理)

相关推荐
夜郎king1 小时前
Spring AI 对接大模型开发易错点总结与实战解决办法
java·人工智能·spring
oradh2 小时前
Oracle数据库中的Java概述
java·数据库·oracle·sql基础·oracle数据库java概述
组合缺一2 小时前
Java AI 框架三国杀:Solon AI vs Spring AI vs LangChain4j 深度对比
java·人工智能·spring·ai·langchain·llm·solon
c++之路2 小时前
适配器模式(Adapter Pattern)
java·算法·适配器模式
吴声子夜歌2 小时前
Java——接口的细节
java·开发语言·算法
阿拉金alakin3 小时前
深入理解 Java 锁机制:CAS 原理、synchronized 优化与主流锁策略全总结
java·开发语言
myheartgo-on3 小时前
Java—方 法
java·开发语言·算法·青少年编程
雨落在了我的手上3 小时前
如何学习java?
java·开发语言·学习
范什么特西3 小时前
计算机杂记
java