SpringMVC 完整执行流程

SpringMVC 完整执行流程

核心组件:DispatcherServlet(前端控制器)、HandlerMappingHandlerAdapterHandler(Controller)ModelAndViewViewResolverView

整体流程图文字版

  1. 客户端发送请求 ,请求到达服务器,被 DispatcherServlet 拦截(前端控制器,整个流程入口)
  2. DispatcherServlet 将请求交给 HandlerMapping
  3. HandlerMapping :根据 URL 查找对应的处理器 Handler(Controller),返回 HandlerExecutionChain(包含 handler 对象 + 拦截器链)
  4. DispatcherServlet 拿到 Handler,去找 HandlerAdapter(处理器适配器,适配不同 Controller 调用方式)
  5. HandlerAdapter 执行目标 Handler(Controller) 业务方法
  6. Controller 处理业务,返回 ModelAndView(封装模型数据 Model + 逻辑视图名 ViewName)
  7. HandlerAdapter 将 ModelAndView 返回给 DispatcherServlet
  8. DispatcherServlet 把逻辑视图名交给 ViewResolver 视图解析器
  9. ViewResolver :把逻辑视图名解析成真正的 View 视图对象(如 jsp、thymeleaf)
  10. DispatcherServlet 调用 View.render() 渲染视图,传入 Model 数据
  11. 视图渲染,把模型数据填充到页面,生成响应
  12. 返回响应给浏览器

详细分步拆解(带源码调用顺序)

View视图对象 ViewResolver视图解析器 Controller业务处理器 HandlerAdapter处理器适配器 HandlerExecutionChain处理器执行链(Handler+拦截器) HandlerMapping处理器映射器 DispatcherServlet前端控制器 Tomcat容器 浏览器(Client) View视图对象 ViewResolver视图解析器 Controller业务处理器 HandlerAdapter处理器适配器 HandlerExecutionChain处理器执行链(Handler+拦截器) HandlerMapping处理器映射器 DispatcherServlet前端控制器 Tomcat容器 浏览器(Client) #mermaid-svg-CL8MPoy72SvCZ66b{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CL8MPoy72SvCZ66b .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CL8MPoy72SvCZ66b .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CL8MPoy72SvCZ66b .error-icon{fill:#552222;}#mermaid-svg-CL8MPoy72SvCZ66b .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CL8MPoy72SvCZ66b .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CL8MPoy72SvCZ66b .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CL8MPoy72SvCZ66b .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CL8MPoy72SvCZ66b .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CL8MPoy72SvCZ66b .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CL8MPoy72SvCZ66b .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CL8MPoy72SvCZ66b .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CL8MPoy72SvCZ66b .marker.cross{stroke:#333333;}#mermaid-svg-CL8MPoy72SvCZ66b svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CL8MPoy72SvCZ66b p{margin:0;}#mermaid-svg-CL8MPoy72SvCZ66b .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-CL8MPoy72SvCZ66b text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-CL8MPoy72SvCZ66b .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-CL8MPoy72SvCZ66b .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-CL8MPoy72SvCZ66b .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-CL8MPoy72SvCZ66b .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-CL8MPoy72SvCZ66b #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-CL8MPoy72SvCZ66b .sequenceNumber{fill:white;}#mermaid-svg-CL8MPoy72SvCZ66b #sequencenumber{fill:#333;}#mermaid-svg-CL8MPoy72SvCZ66b #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-CL8MPoy72SvCZ66b .messageText{fill:#333;stroke:none;}#mermaid-svg-CL8MPoy72SvCZ66b .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-CL8MPoy72SvCZ66b .labelText,#mermaid-svg-CL8MPoy72SvCZ66b .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-CL8MPoy72SvCZ66b .loopText,#mermaid-svg-CL8MPoy72SvCZ66b .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-CL8MPoy72SvCZ66b .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-CL8MPoy72SvCZ66b .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-CL8MPoy72SvCZ66b .noteText,#mermaid-svg-CL8MPoy72SvCZ66b .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-CL8MPoy72SvCZ66b .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-CL8MPoy72SvCZ66b .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-CL8MPoy72SvCZ66b .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-CL8MPoy72SvCZ66b .actorPopupMenu{position:absolute;}#mermaid-svg-CL8MPoy72SvCZ66b .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-CL8MPoy72SvCZ66b .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-CL8MPoy72SvCZ66b .actor-man circle,#mermaid-svg-CL8MPoy72SvCZ66b line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-CL8MPoy72SvCZ66b :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 执行拦截器 preHandle() 执行拦截器 postHandle() 执行拦截器 afterCompletion() 发送HTTP请求doService() → doDispatch()getHandler(request) 查询处理器封装Handler + 拦截器集合返回 HandlerExecutionChaingetHandlerAdapter(handler) 获取适配器调用Controller业务方法返回 ModelAndView(模型+逻辑视图名)返回 ModelAndViewresolveViewName(逻辑视图名)解析生成View物理视图对象返回 View对象render(model,request,response) 渲染视图把Model数据写入Request域,输出页面返回HTTP响应结果

① 请求进入 DispatcherServlet

web 容器(Tomcat)收到 http 请求,匹配到 SpringMVC 的核心 Servlet:DispatcherServlet,执行 doService()doDispatch()doDispatch 是 SpringMVC 核心调度方法

java 复制代码
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception

② 获取执行链:HandlerMapping

java 复制代码
// 1. 根据request获取 HandlerExecutionChain
HandlerExecutionChain mappedHandler = getHandler(request);
  • HandlerMapping 作用:URL → Controller映射

  • 返回 HandlerExecutionChain:包含 handler(controller对象) + 拦截器 HandlerInterceptor 集合

  • 如果找不到handler,返回404。

    拦截器执行时机:preHandle() 在Controller方法执行之前执行。

③ 获取适配器 HandlerAdapter

java 复制代码
// 2. 根据handler拿到可以执行它的适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

为什么需要适配器?

SpringMVC支持多种handler实现方式:注解@Controller、HttpRequestHandler、SimpleControllerHandlerAdapter,不同接口方法不一样;适配器统一对外调用接口,DispatcherServlet不需要关心controller是什么类型。

④ 执行Controller业务逻辑

java 复制代码
// 调用handler,执行controller方法,返回ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  • HandlerAdapter 调用 controller 的业务方法
  • 业务处理,封装数据到 Model,设置视图名称,返回 ModelAndView

如果是 @ResponseBody,直接返回json,ModelAndView为null,跳过视图解析阶段

⑤ 拦截器 postHandle

Controller方法执行完毕后,执行拦截器的 postHandle(),在视图渲染之前

⑥ 视图解析 ViewResolver

java 复制代码
// 3. 视图解析,渲染页面
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

内部:

java 复制代码
View view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
  • ViewResolver:接收逻辑视图名 ,拼接前后缀,得到物理视图,生成View对象
    例:逻辑视图 list,前缀/WEB-INF/views/,后缀.jsp/WEB-INF/views/list.jsp

⑦ 视图渲染 View#render()

java 复制代码
view.render(mv.getModelInternal(), request, response);
  • 将 Model 中的数据放入 request域
  • jsp/模板引擎渲染页面,生成输出流

⑧ 拦截器 afterCompletion

视图渲染完成之后,执行拦截器 afterCompletion(),做资源清理。

⑨ 响应返回浏览器


两种分支情况

  1. 普通页面跳转(返回视图):完整走完上面全部流程,解析视图、渲染页面。

  2. @ResponseBody / RestController(返回JSON)

    • Controller执行完,直接把对象转json写入response输出流
    • 返回的 ModelAndView = null,跳过视图解析、视图渲染

    Controller HandlerAdapter(RequestResponseBody处理器) HandlerMapping DispatcherServlet 浏览器(Client) Controller HandlerAdapter(RequestResponseBody处理器) HandlerMapping DispatcherServlet 浏览器(Client) #mermaid-svg-0eT8os7CFSweJoc5{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0eT8os7CFSweJoc5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0eT8os7CFSweJoc5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0eT8os7CFSweJoc5 .error-icon{fill:#552222;}#mermaid-svg-0eT8os7CFSweJoc5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0eT8os7CFSweJoc5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0eT8os7CFSweJoc5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0eT8os7CFSweJoc5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0eT8os7CFSweJoc5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0eT8os7CFSweJoc5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0eT8os7CFSweJoc5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0eT8os7CFSweJoc5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0eT8os7CFSweJoc5 .marker.cross{stroke:#333333;}#mermaid-svg-0eT8os7CFSweJoc5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0eT8os7CFSweJoc5 p{margin:0;}#mermaid-svg-0eT8os7CFSweJoc5 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-0eT8os7CFSweJoc5 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-0eT8os7CFSweJoc5 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-0eT8os7CFSweJoc5 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-0eT8os7CFSweJoc5 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-0eT8os7CFSweJoc5 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-0eT8os7CFSweJoc5 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-0eT8os7CFSweJoc5 .sequenceNumber{fill:white;}#mermaid-svg-0eT8os7CFSweJoc5 #sequencenumber{fill:#333;}#mermaid-svg-0eT8os7CFSweJoc5 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-0eT8os7CFSweJoc5 .messageText{fill:#333;stroke:none;}#mermaid-svg-0eT8os7CFSweJoc5 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-0eT8os7CFSweJoc5 .labelText,#mermaid-svg-0eT8os7CFSweJoc5 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-0eT8os7CFSweJoc5 .loopText,#mermaid-svg-0eT8os7CFSweJoc5 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-0eT8os7CFSweJoc5 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-0eT8os7CFSweJoc5 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-0eT8os7CFSweJoc5 .noteText,#mermaid-svg-0eT8os7CFSweJoc5 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-0eT8os7CFSweJoc5 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-0eT8os7CFSweJoc5 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-0eT8os7CFSweJoc5 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-0eT8os7CFSweJoc5 .actorPopupMenu{position:absolute;}#mermaid-svg-0eT8os7CFSweJoc5 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-0eT8os7CFSweJoc5 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-0eT8os7CFSweJoc5 .actor-man circle,#mermaid-svg-0eT8os7CFSweJoc5 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-0eT8os7CFSweJoc5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} preHandle拦截器 postHandle、afterCompletion HTTP请求查询Handler返回执行链执行handler调用Controller方法返回Java对象对象序列化为JSON,直接写入response输出流ModelAndView = null直接返回JSON响应,跳过视图解析

组件职责总结

组件 作用
DispatcherServlet 前端控制器,总调度,整个MVC的入口
HandlerMapping 请求URL映射到Controller,返回执行链
Handler(Controller) 业务处理器,处理业务逻辑
HandlerAdapter 适配器,统一调用不同类型Controller
ModelAndView 封装模型数据+逻辑视图名称
ViewResolver 视图解析器:逻辑视图 → 物理View对象
View 视图对象,负责渲染输出页面
HandlerInterceptor 拦截器,在controller前后做增强处理

时序记忆口诀

请求找前端控制器,

映射器找处理器链,

适配器调用控制器,

返回模型和视图名,

解析器解析真实视图,

视图渲染输出响应。

面试常问

  1. DispatcherServlet 在什么时候初始化?

    tomcat启动,容器初始化servlet时,加载springmvc.xml,初始化各个HandlerMapping、HandlerAdapter等组件。

  2. Filter 和 Interceptor 区别?

  • Filter:Servlet规范,在DispatcherServlet之前执行;
  • Interceptor:SpringMVC组件,在DispatcherServlet内部,Controller方法前后执行。
  1. @ResponseBody为什么不跳转页面?

    RequestResponseBodyMethodProcessor 适配器处理,直接写response,modelAndView置null,不进入视图解析。

文字时序梳理(面试口述版)

  1. 浏览器发起HTTP请求,Tomcat接收请求交给DispatcherServletdoDispatch()

  2. DispatcherServlet调用HandlerMapping,根据url得到HandlerExecutionChain(Controller+拦截器)

  3. 执行拦截器preHandle()

  4. DispatcherServlet通过getHandlerAdapter()拿到适配当前Controller的HandlerAdapter

  5. HandlerAdapter调用Controller业务方法,得到ModelAndView

  6. 执行拦截器postHandle()

  7. 将逻辑视图名交给ViewResolver解析,得到真实View对象

  8. 调用View.render()渲染,填充Model数据

  9. 执行拦截器afterCompletion()做资源清理

  10. 响应返回浏览器

    特殊:@ResponseBody场景,适配器直接序列化写response,ModelAndView=null跳过视图解析与渲染

相关推荐
蒲锘2 小时前
DevOps 实验阶段四:Infra 节点 + MySQL + Spring Boot 容器化
spring boot·mysql·docker
咖啡八杯2 小时前
统一返回对象设计:AjaxResult 与 R 的取舍
java·spring boot·框架·若依·返回值
运维全栈笔记5 小时前
Vue + Spring Boot 前后端分离项目部署笔记(若依 RuoYi-Vue 3.9.2)
运维·服务器·vue.js·spring boot·笔记·开源·开源软件
凤山老林12 小时前
Spring Boot 3.x AOT 编译实战:从原理、踩坑到生产落地
java·spring boot·后端
博傅12 小时前
Spring 核心原理
java·后端·spring
玹外之音13 小时前
Spring AI + Elasticsearch 向量存储实战:从零构建智能文档检索系统
人工智能·spring·elasticsearch
摇滚侠13 小时前
《SpringBoot 3:入门与应用实战》第 5 章 使用 Spring Boot 阅读笔记 9
java·spring boot·笔记
进阶的小名13 小时前
Spring AI 2.0 探索:多 OpenAI-Compatible 模型接入,以及下一代 Session 记忆管理
java·人工智能·后端·gpt·spring·ai·chatgpt
wno70417 小时前
Spring Boot中使用Servlet
spring boot·后端·servlet