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 链式解析。

相关推荐
luck_bor15 分钟前
File类&递归作业
java·开发语言
武子康34 分钟前
Java-07 深入浅出 MyBatis数据库一对多关系模型实战:表结构设计与查询实现
java·后端
花椒技术1 小时前
企业内部 Agent 落地复盘:Gateway、Skill 和二次确认如何串起受控业务执行
后端·agent·ai编程
REDcker3 小时前
Linux OverlayFS详解
java·linux·运维
Royzst3 小时前
xml知识点
java·服务器·前端
我是一颗柠檬3 小时前
【MySQL全面教学】MySQL事务与ACID Day9(2026年)
数据库·后端·mysql
枕星而眠3 小时前
数据结构八大排序详解(一):四大简单排序
c语言·数据结构·c++·后端
IT_陈寒3 小时前
React useEffect闭包陷阱差点把我整失业了
前端·人工智能·后端
鱼鳞_3 小时前
苍穹外卖-Day08(缓存套餐)
java·redis·缓存
过期动态4 小时前
【LeetCode 热题 100】移动零
java·数据结构·算法·leetcode·职场和发展·rabbitmq