Spring源码 第六篇:Spring 5 源码深度拆解:SpringMVC 全流程核心原理

引言

接下来基于 Spring 源码,主要介绍以下几个问题:

  1. @EnableWebMvc 到底干了什么?
  2. 请求从浏览器进来,完整经过哪些组件?
  3. HandlerMappingHandlerAdapterViewResolver 分别干嘛用?
  4. 参数是怎么绑定到方法入参的?
  5. 返回值是怎么变成 JSON / 页面 / 异常信息的?
  6. 拦截器、异常处理器在源码中哪个节点执行?

一、核心前提:SpringMVC 整体架构

1. 一句话概括流程

浏览器请求 → DispatcherServlet 分发 → 找到对应 Controller 方法 → 执行方法 → 处理返回值 → 响应给浏览器。

2. 九大核心组件(面试必背)

组件 作用
HandlerMapping 根据请求 URL 找到对应的 Controller 方法
HandlerAdapter 统一执行 Controller 方法的适配器
ThemeResolver 不同主题间动态切换
ViewResolver 视图解析器,把逻辑视图名转为真实视图
HandlerExceptionResolver 全局异常处理器
LocaleResolver 国际化解析器
MultipartResolver 文件上传解析器
RequestToViewNameTranslator 默认视图名转换器
FlashMapManager 重定向数据管理器

二、入口:@EnableWebMvc 源码解析

@EnableWebMvc 是 JavaConfig 方式手动启用 SpringMVC 的注解,等价于 XML 中的 <mvc:annotation-driven/>

注意:Spring Boot 项目不推荐、也不需要加它。

java 复制代码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

核心就是导入:DelegatingWebMvcConfiguration

它会自动向容器注册:

  • RequestMappingHandlerMapping
  • RequestMappingHandlerAdapter
  • ExceptionHandlerExceptionResolver
  • 各种参数解析器、返回值处理器
  • 消息转换器(MappingJackson2HttpMessageConverter

一句话@EnableWebMvc = 自动装配 SpringMVC 全套核心组件。

三、容器启动:SpringMVC 初始化流程

1. Tomcat 启动 → 加载 DispatcherServlet

DispatcherServlet 本质是一个 Servlet,生命周期:init()service()destroy()

2. init 阶段核心:初始化九大组件

java 复制代码
protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context);     // 文件上传
    initLocaleResolver(context);        // 国际化
    initThemeResolver(context);
    initHandlerMappings(context);       // 路由映射
    initHandlerAdapters(context);       // 执行适配器
    initHandlerExceptionResolvers(context); // 异常处理
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);         // 视图解析
    initFlashMapManager(context);
}

这就是 SpringMVC 启动时干的所有事。

四、请求执行全流程

浏览器发送 HTTP 请求
Tomcat 接收请求,交给 DispatcherServlet
doService(req, resp)
doDispatch(req, resp)

【核心总调度方法】

  1. getHandler(request)

遍历 HandlerMapping,找到对应 Controller 方法
2. getHandlerAdapter(handler)

找到能执行该方法的适配器
3. 执行拦截器 applyPreHandle

调用 preHandle
4. adapter.handle(request, response, handler)

真正执行 Controller 方法
内部:参数解析 → 执行方法 → 得到返回值
5. 处理返回值

(JSON/视图/文件等)
6. 执行拦截器 applyPostHandle

调用 postHandle
7. 异常处理

(执行异常解析器)
8. 渲染视图

(如果不是直接响应)
9. 执行拦截器 triggerAfterCompletion

调用 afterCompletion
响应回浏览器

五、核心方法 doDispatch 源码解析

doDispatch 源码部分内容如下:

java 复制代码
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;

    try {
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {
            // 1. 找到对应的处理器(Controller方法)
            mappedHandler = getHandler(processedRequest);

            // 2. 找到适配器
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // 3. 执行拦截器 preHandle
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // 4. 【真正调用Controller方法】
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            // 5. 执行拦截器 postHandle
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
            dispatchException = ex;
        }

        // 6. 处理结果、渲染视图、异常处理
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
        // 最终拦截器
        mappedHandler.triggerAfterCompletion(processedRequest, response, ex);
    }
}

六、关键节点深度拆解

1. 根据 URL 找到 Controller 方法

流程图位置doDispatch1. getHandler(request)

  • 所在类org.springframework.web.servlet.DispatcherServlet
  • 所在方法doDispatch
  • 核心代码mappedHandler = getHandler(processedRequest);

核心实现

  • 遍历容器中所有 HandlerMapping
  • RequestMappingHandlerMapping 负责匹配
  • 建立并返回 urlHandlerMethod 的映射
  • 封装为 HandlerExecutionChain(包含所有拦截器)

2. 找到执行方法的适配器

流程图位置doDispatch2. getHandlerAdapter(handler)

  • 所在类DispatcherServlet
  • 所在方法doDispatch
  • 核心代码HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

核心实现

  • 遍历所有 HandlerAdapter
  • 匹配到 RequestMappingHandlerAdapter 继承自 AbstractHandlerMethodAdapter
  • 用于统一执行 @RequestMapping 方法

3. 执行拦截器 preHandle

流程图位置doDispatch3. 执行拦截器 preHandle

  • 所在类DispatcherServlet
  • 所在方法doDispatch
  • 核心代码
java 复制代码
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    return;
}

实际执行类HandlerExecutionChain

java 复制代码
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    for (int i = 0; i < this.interceptors.size(); i++) {
        HandlerInterceptor interceptor = this.interceptors.get(i);
        if (!interceptor.preHandle(request, response, this.handler)) {
            triggerAfterCompletion(request, response, null);
            return false;
        }
        this.interceptorIndex = i;
    }
    return true;
}

关键点 :任一拦截器返回 false,直接中断请求,并执行已通过拦截器的 afterCompletion

4. 真正执行 Controller 方法

流程图位置doDispatch4. adapter.handle(...)

  • 所在类DispatcherServlet
  • 所在方法doDispatch
  • 核心代码mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

实际执行类RequestMappingHandlerAdapter

4.1 参数解析

调度入口RequestMappingHandlerAdapter
执行类ServletInvocableHandlerMethod

遍历所有参数解析器,处理:

  • @RequestParam
  • @PathVariable
  • @RequestBody
  • @RequestHeader
  • ServletRequest / HttpServletResponse
4.2 反射执行目标方法

所在类InvocableHandlerMethod
方法doInvoke

java 复制代码
return method.invoke(getBean(), getArguments());

执行链路:

复制代码
ha.handle()
    ↓
handleInternal()
    ↓
invokeHandlerMethod()
    ↓
invocableMethod.invokeAndHandle()
    ↓
invokeForRequest()
    ↓
【doInvoke()】 ← 最终执行到这里
    ↓
method.invoke(bean, args) → 反射调用Controller方法

5. 处理返回值(JSON / 视图)

流程图位置 : 执行方法后 → 5. 处理返回值

  • 所在类ServletInvocableHandlerMethod
  • 方法invokeAndHandle
  • 核心组件HandlerMethodReturnValueHandlerComposite

常见实现

  • RequestResponseBodyMethodProcessor:处理 @ResponseBody → JSON
  • ViewNameMethodReturnValueHandler:处理字符串视图名
  • ModelAndViewMethodReturnValueHandler:处理 ModelAndView

6. 执行拦截器 postHandle

流程图位置doDispatch6. 执行拦截器 postHandle

  • 所在类DispatcherServlet
  • 所在方法doDispatch
  • 核心代码mappedHandler.applyPostHandle(processedRequest, response, mv);

实际执行类HandlerExecutionChain

java 复制代码
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
    for (int i = this.interceptors.size() - 1; i >= 0; i--) {
        HandlerInterceptor interceptor = this.interceptors.get(i);
        interceptor.postHandle(request, response, this.handler, mv);
    }
}

注意postHandle逆序执行。

  • preHandle:正序执行
  • postHandle:逆序执行
  • afterCompletion:逆序执行

7. 渲染视图

流程图位置doDispatch7. 渲染视图

  • 所在类DispatcherServlet
  • 所在方法processDispatchResult
  • 核心代码render(mv, request, response);

逻辑

  • 通过 ViewResolver 解析视图名得到 View
  • 执行 view.render (model, request, response)
  • 适用于 JSP/Thymeleaf/Freemarker 等模板

8. 执行拦截器 afterCompletion

流程图位置doDispatch8. 执行拦截器 afterCompletion

  • 所在类DispatcherServlet
  • 位置doDispatch 最终 catch
  • 核心代码mappedHandler.triggerAfterCompletion(processedRequest, response, ex);

实际执行类HandlerExecutionChain

java 复制代码
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception {
    for (int i = this.interceptorIndex; i >= 0; i--) {
        HandlerInterceptor interceptor = this.interceptors.get(i);
        interceptor.afterCompletion(request, response, this.handler, ex);
    }
}

关键点 :无论正常返回还是抛异常,一定执行

七、高频面试题

  1. SpringMVC 执行流程是什么?

    1. Tomcat 启动,初始化 DispatcherServlet
    2. 加载 Spring 容器,注册所有 SpringMVC 组件
    3. 请求进入 doDispatch
    4. 根据 URL 找到 Controller 方法
    5. 执行拦截器前置
    6. 参数解析 → 调用方法
    7. 处理返回值(JSON/视图)
    8. 执行拦截器后置
    9. 渲染/响应
    10. 最终拦截器
  2. DispatcherServlet 作用?

    答:统一入口,总调度,管理九大组件。

  3. HandlerMappingHandlerAdapter 区别?

    答:Mapping 负责找方法,Adapter 负责执行方法。

  4. @ResponseBody 原理?

    答:返回值处理器 + JSON 消息转换器。

  5. 拦截器和过滤器区别?

    • Filter:Servlet 规范,所有容器都支持,拦截所有请求。
    • Interceptor:SpringMVC 组件,能访问 Spring 容器。
  6. 参数是如何注入的?

    答:ArgumentResolver 链式解析。

相关推荐
是小李呀3 分钟前
Java集合扩容机制解析:ArrayList与HashMap底层原理及性能差异
java
SimonKing3 分钟前
FreeLLMAPI:24 家厂商、128 个模型、1 个 Key智能切换所有可用的模型
java·后端·程序员
阿pin4 分钟前
Java随笔-JVM内存模型
java·开发语言·jvm·内存模型
AI 大模型学习不踩坑22 分钟前
Harness 架构原理与工程实践详解
java·人工智能·神经网络·机器学习·计算机视觉·架构
IT_陈寒1 小时前
React useEffect里面写异步,我的状态怎么老丢?
前端·人工智能·后端
深盾科技_Virbox1 小时前
软件交付保护如何从加壳走向代码虚拟化
java·python·安全
学计算机的计算基1 小时前
二叉树算法下篇:递归核心技巧与高频面试题详解
java·笔记·算法
Liquad Li1 小时前
Salesforce高级开发面试题
java·网络·面试·crm·salesforce
Zhou1411361 小时前
Java常见面试题7
java·开发语言