14. SpringMVC执行流程
14.1、SpringMVC 常用组件
- DispatcherServlet:前端控制器,不需要工程师开发,由框架提供
作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求
- HandlerMapping:处理器映射器,不需要工程师开发,由框架提供
作用:根据请求的 url、method 等信息查找 Handler,即控制器方法
- Handler:处理器,需要工程师开发
作用:在 DispatcherServlet 的控制下 Handler 对具体的用户请求进行处理
- HandlerAdapter:**处理器适配器**,不需要工程师开发,由框架提供
作用:通过 HandlerAdapter 对处理器(控制器方法)进行执行
- ViewResolver:视图解析器,不需要工程师开发,由框架提供
作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、
RedirectView
- View:视图
作用:将模型数据通过页面展示给用户
14.2、DispatcherServlet 初始化过程
DispatcherServlet 本质上是一个 Servlet,所以天然的遵循 Servlet 的生命周期。所以宏观上是 Servlet 生命周期来进行调度。
① 初始化 WebApplicationContext
所在类:org.springframework.web.servlet.FrameworkServlet
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac =(ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
// 创建WebApplicationContext
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
synchronized (this.onRefreshMonitor) {
// 刷新WebApplicationContext
onRefresh(wac);
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
// 将IOC容器在应用域共享
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
② 创建 WebApplicationContext
所在类:org.springframework.web.servlet.FrameworkServlet
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
Class<?> contextClass = getContextClass();
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass))
{
throw new ApplicationContextException("Fatal initialization error in servlet with name '" +getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
// 通过反射创建 IOC 容器对象
ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
// 设置父容器
wac.setParent(parent);
String configLocation = getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
configureAndRefreshWebApplicationContext(wac);
return wac;
}
③DispatcherServlet 初始化策略
FrameworkServlet 创建 WebApplicationContext 后,刷新容器,调用 onRefresh(wac),此方法在
DispatcherServlet 中进行了重写,调用了 initStrategies(context)方法,初始化策略,即初始化
DispatcherServlet 的各个组件
所在类:org.springframework.web.servlet.DispatcherServlet
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
14.3、DispatcherServlet 调用组件处理请求
①processRequest()
FrameworkServlet 重写 HttpServlet 中的 service()和 doXxx(),这些方法中调用了
processRequest(request, response)
所在类:org.springframework.web.servlet.FrameworkServlet
protected final void processRequest(HttpServletRequest request,HttpServletResponse response)throws ServletException, IOException
{
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request,response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(),new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
try {
// 执行服务,doService()是一个抽象方法,在DispatcherServlet中进行了重写
doService(request, response);
}
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
②doService()
所在类:org.springframework.web.servlet.DispatcherServlet
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName,request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE,getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request,response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE,Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
RequestPath requestPath = null;
if (this.parseRequestPath && !ServletRequestPathUtils.hasParsedRequestPath(request)) {
requestPath = ServletRequestPathUtils.parseAndCache(request);
}
try {
// 处理请求和响应
doDispatch(request, response);
}
finally {
if
(!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
if (requestPath != null) {
ServletRequestPathUtils.clearParsedRequestPath(request);
}
}
}
③doDispatch()
所在类:org.springframework.web.servlet.DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
/*
mappedHandler:调用链
包含handler、interceptorList、interceptorIndex
handler:浏览器发送的请求所匹配的控制器方法
interceptorList:处理控制器方法的所有拦截器集合
interceptorIndex:拦截器索引,控制拦截器afterCompletion()的执行
*/
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 通过控制器方法创建相应的处理器适配器,调用所对应的控制器方法
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request,mappedHandler.getHandler());
if (new ServletWebRequest(request,response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 调用拦截器的preHandle()
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// 由处理器适配器调用具体的控制器方法,最终获得ModelAndView对象
mv = ha.handle(processedRequest, response,mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
// 调用拦截器的postHandle()
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and otherscenarios.
dispatchException = new NestedServletException("Handler dispatchfailed", err);
}
// 后续处理:处理模型数据和渲染视图
processDispatchResult(processedRequest, response, mappedHandler, mv,dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processingfailed",
err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
④processDispatchResult()
private void processDispatchResult(HttpServletRequest request,HttpServletResponse response,@Nullable HandlerExecutionChain
mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered",exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler(): null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
// 处理模型数据和渲染视图
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
// Exception (if any) is already handled..
// 调用拦截器的afterCompletion()
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
14.4、SpringMVC 的执行流程
\1) 用户向服务器发送请求,请求被 SpringMVC 前端控制器 DispatcherServlet 捕获。
\2) DispatcherServlet 对请求 URL 进行解析,得到请求资源标识符(URI),判断请求 URI 对应的映射:
a) 不存在
i. 再判断是否配置了 mvc:default-servlet-handler
ii. 如果没配置,则控制台报映射查找不到,客户端展示 404 错误
iii. 如果有配置,则访问目标资源(一般为静态资源,如:JS,CSS,HTML),找不到客户端也会展示 404
错误
b) 存在则执行下面的流程
\3) 根据该 URI,调用 HandlerMapping 获得该 Handler 配置的所有相关的对象(包括 Handler 对象以及
Handler 对象对应的拦截器),最后以 HandlerExecutionChain 执行链对象的形式返回。
\4) DispatcherServlet 根据获得的 Handler,选择一个合适的 HandlerAdapter。
\5) 如果成功获得 HandlerAdapter,此时将开始执行拦截器的 preHandler(…)方法【正向】
\6) 提取 Request 中的模型数据,填充 Handler 入参,开始执行 Handler(Controller)方法,处理请求。
在填充 Handler 的入参过程中,根据你的配置,Spring 将帮你做一些额外的工作:
a) HttpMessageConveter: 将请求消息(如 Json、xml 等数据)转换成一个对象,将对象转换为指定
的响应信息
b) 数据转换:对请求消息进行数据转换。如 String 转换成 Integer、Double 等
c) 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
d) 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到 BindingResult 或 Error 中
\7) Handler 执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象。
\8) 此时将开始执行拦截器的 postHandle(...)方法【逆向】。
\9) 根据返回的 ModelAndView(此时会判断是否存在异常:如果存在异常,则执行
HandlerExceptionResolver 进行异常处理)选择一个适合的 ViewResolver 进行视图解析,根据 Model
和 View,来渲染视图。
\10) 渲染视图完毕执行拦截器的 afterCompletion(…)方法【逆向】。
\11) 将渲染结果返回给客户端。
14. SpringMVC执行流程的更多相关文章
- springmvc执行流程详细介绍
1.什么是MVC MVC是Model View Controller的缩写,它是一个设计模式 2.springmvc执行流程详细介绍 第一步:发起请求到前端控制器(DispatcherServlet) ...
- springMVC执行流程及原理
spring的MVC执行原理 1.spring mvc将所有的请求都提交给DispatcherServlet,它会委托应用系统的其他模块负责对请求 进行真正的处理工作. 2.DispatcherSer ...
- 2.SpringMVC执行流程
SpringMVC 执行流程: 执行流程简单分析: 1.浏览器提交请求到中央调度器 2.中央调度器直接将请求转给处理器映射器 3.处理器映射器会根据请求,找到处理该请求的处理器,并将其封装为处理器执行 ...
- SpringMvc执行流程及底层代码流程
SpringMVC执行流程 01.客户端发送请求被我们在web.xml中配置DispatcherServlet(核心控制器)拦截: 默认执行DispatcherServlet中的 protecte ...
- 面试高频SpringMVC执行流程最优解(源码分析)
文章已托管到GitHub,大家可以去GitHub查看阅读,欢迎老板们前来Star! 搜索关注微信公众号 码出Offer 领取各种学习资料! SpringMVC执行流程 SpringMVC概述 Spri ...
- SpringMVC执行流程总结
SpringMVC 执行流程: 用户发送请求至前端控制器 DispatcherServlet DispatcherServlet 收到请求调用处理映射器 HandlerMapping 处理映射器根据请 ...
- 【阅读SpringMVC源码】手把手带你debug验证SpringMVC执行流程
✿ 阅读源码思路: 先跳过非重点,深入每个方法,进入的时候可以把整个可以理一下方法的执行步骤理一下,也可以,理到某一步,继续深入,回来后,接着理清除下面的步骤. ✿ 阅读本文的准备工作,预习一下Spr ...
- Servlet、Struts2、SpringMVC执行流程
Servlet 有以下四个阶段: 1.加载和实例化 Servlet容器负责加载和实例化Servlet. 当Servlet容器启动时,或者在容器检测到需要这个Servlet来响应第一个请求时,创建Ser ...
- SSM-SpringMVC-01:SpringMVC是什么?SpringMVC执行流程
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- SpringMVC简介: SpringMVC也叫Spring Web mvc,属于表现层的框架.Sprin ...
- springMVC执行流程及架构
目录 springMVC简单执行流程 springMVC框架 注解实现 springMVC简单执行流程 springMVC框架 执行流程: 1.用户发送请求至前端控制器DispatcherServle ...
随机推荐
- Flutter 下载篇 - 叁 | 网络库切换实践与思考
前言 本文是关于使用flutter_download_manager下载功能的实践和探索.我们将基于flutter_download_manager的功能扩展,改造成自己想要的样子.在阅读本文之前,建 ...
- [PKM]阅读的方法
0 概述 数据 => 信息 => 知识 => 智慧 1 读书的目的 : 先寻求真理,而后实践 => 先博学,而后守约(读透) & 先泛读/速读,再精读 / 知行合一 年 ...
- 【SpringCloud】(三)Hystrix 与 Zuul
5 Hystrix Hystrix:一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖会不可避免得调用失败,比如超时.异常等,Hystrix能保证在一个依赖出问题的情况下,不会导致整 ...
- 2022CSP游记
目录 CSP-J2022 7:45 8:15 8:27 8:38 9:12 9:23 10:34 11:57 中午 CSP-S2022 2:27 4:15 6:12 估分 普及 提高 自查 出分 废物 ...
- pytest的fixture
1 什么是fixture @pytest.fixture def my_fruit(): return Fruit("apple") 如上,用@pytest.fixture装饰的函 ...
- 关于Python异步协程中for循环的使用
本文转载自简书:https://www.jianshu.com/p/c321eb22cffd 用户:简单书写_, Python使用异步模块Asyncio实现多线程并发,一般方式是: async def ...
- nuxt下运行项目时内存溢出(out of memory)的一种情况
话不多说直接上代码: 如图,点红点的三行引入了一个组件,内容是同意注册协议的弹窗.但是在run dev的时候提示说内存溢出了(out of memory)...经过多方排查,定位到这个组件,警察叔叔就 ...
- #Python基础 DateFrame 查看数据信息
一:导入案例数据及X-MIND 二:实例 2.1 显示摘要信息 2.2显示描述性统计信息 2.3显示 前后n行 2.4显示索引.列信息 2.5显示每列的数据类型
- java线程池和多线程的使用详解
Java 多线程和线程池使用 java多线程实现的几种方法 1.继承Thread类 继承Thread类,重写run方法,创建线程类对象调用start方法启动线程. public class Threa ...
- 2022-10-04:以下go语言代码输出什么?A:{123} main.T{x:123} B:{123} T{x:123} C:boo boo D:boo main.T{x:123}。 packag
2022-10-04:以下go语言代码输出什么?A:{123} main.T{x:123} B:{123} T{x:123} C:boo boo D:boo main.T{x:123}. packag ...