SpringMVC 完整执行流程
核心组件:
DispatcherServlet(前端控制器)、HandlerMapping、HandlerAdapter、Handler(Controller)、ModelAndView、ViewResolver、View

整体流程图文字版
- 客户端发送请求 ,请求到达服务器,被
DispatcherServlet拦截(前端控制器,整个流程入口) DispatcherServlet将请求交给 HandlerMapping- HandlerMapping :根据 URL 查找对应的处理器
Handler(Controller),返回HandlerExecutionChain(包含 handler 对象 + 拦截器链) DispatcherServlet拿到 Handler,去找 HandlerAdapter(处理器适配器,适配不同 Controller 调用方式)- HandlerAdapter 执行目标
Handler(Controller)业务方法 - Controller 处理业务,返回 ModelAndView(封装模型数据 Model + 逻辑视图名 ViewName)
- HandlerAdapter 将 ModelAndView 返回给 DispatcherServlet
DispatcherServlet把逻辑视图名交给 ViewResolver 视图解析器- ViewResolver :把逻辑视图名解析成真正的
View视图对象(如 jsp、thymeleaf) DispatcherServlet调用View.render()渲染视图,传入 Model 数据- 视图渲染,把模型数据填充到页面,生成响应
- 返回响应给浏览器
详细分步拆解(带源码调用顺序)
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(),做资源清理。
⑨ 响应返回浏览器
两种分支情况
-
普通页面跳转(返回视图):完整走完上面全部流程,解析视图、渲染页面。
-
@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前后做增强处理 |
时序记忆口诀
请求找前端控制器,
映射器找处理器链,
适配器调用控制器,
返回模型和视图名,
解析器解析真实视图,
视图渲染输出响应。
面试常问
-
DispatcherServlet 在什么时候初始化?
tomcat启动,容器初始化servlet时,加载springmvc.xml,初始化各个HandlerMapping、HandlerAdapter等组件。
-
Filter 和 Interceptor 区别?
- Filter:Servlet规范,在DispatcherServlet之前执行;
- Interceptor:SpringMVC组件,在DispatcherServlet内部,Controller方法前后执行。
-
@ResponseBody为什么不跳转页面?
RequestResponseBodyMethodProcessor 适配器处理,直接写response,modelAndView置null,不进入视图解析。
文字时序梳理(面试口述版)
-
浏览器发起HTTP请求,Tomcat接收请求交给
DispatcherServlet的doDispatch() -
DispatcherServlet调用HandlerMapping,根据url得到HandlerExecutionChain(Controller+拦截器) -
执行拦截器
preHandle() -
DispatcherServlet通过getHandlerAdapter()拿到适配当前Controller的HandlerAdapter -
HandlerAdapter调用Controller业务方法,得到ModelAndView -
执行拦截器
postHandle() -
将逻辑视图名交给
ViewResolver解析,得到真实View对象 -
调用
View.render()渲染,填充Model数据 -
执行拦截器
afterCompletion()做资源清理 -
响应返回浏览器
特殊:
@ResponseBody场景,适配器直接序列化写response,ModelAndView=null,跳过视图解析与渲染。