springMVC源码分析--DispatcherServlet请求获取及处理
在之前的博客springMVC源码分析--容器初始化(二)DispatcherServlet中我们介绍过DispatcherServlet,是在容器初始化过程中出现的,我们之前也说过DispatcherServlet其实就是一个HttpServlet,其实他是HttpServlet的子类,所以它和普通的HttpServlet有同样的配置:
<servlet> <servlet-name>springmvc</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc-config.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>*.action</url-pattern> </servlet-mapping>
仅仅把DispatcherServlet当做一个Servlet的话,上面配置的含义就是这个Servlet会被所有的*.action的请求所调用。
既然DispatcherServlet是一个HttpServlet那么它应该会实现HttpServlet提供的如下方法:
当然这些所有的方法的实现是DispatcherServlet的父类FrameworkServlet中实现的。
当然这些实现方法中的默认实现其实是如下的
FrameworkServlet类中
@Override protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }
@Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }
processRequest的实现是在FrameworkServlet中,此方法中最主要的操作就是调用doService方法
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(request, response); } catch (ServletException ex) { failureCause = ex; throw ex; } catch (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(); } if (logger.isDebugEnabled()) { if (failureCause != null) { this.logger.debug("Could not complete request", failureCause); } else { if (asyncManager.isConcurrentHandlingStarted()) { logger.debug("Leaving response open for concurrent processing"); } else { this.logger.debug("Successfully completed request"); } } } publishRequestHandledEvent(request, response, startTime, failureCause); } }
doService方法的最终实现是在DispatcherServlet中,这样所有的Http请求(GET、POST、PUT和DELETE等)的最终操作就DispatcherServlet中实现了。
DispatcherServlet中doService的实现如下,对Request设置了一些全局属性,最终接下来的操作是在doDispatcher函数中实现了。
//获取请求,设置一些request的参数,然后分发给doDispatch @Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { if (logger.isDebugEnabled()) { String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : ""; logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(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<String, Object>(); Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } // Make framework objects available to handlers and view objects. /* 设置web应用上下文**/ 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()); //请求刷新时保存属性 FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } //Flash attributes 在对请求的重定向生效之前被临时存储(通常是在session)中,并且在重定向之后被立即移除 request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); //FlashMap 被用来管理 flash attributes 而 FlashMapManager 则被用来存储,获取和管理 FlashMap 实体. request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); 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); } } } }
doDispatch函数中完成了对一个请求的所有操作,包含的内容还是比较多的,我们就不做详细分解,接下来我们会一步一步的分析一个请求调用Controller的完整过程。
/** *将Handler进行分发,handler会被handlerMapping有序的获得 *通过查询servlet安装的HandlerAdapters来获得HandlerAdapters来查找第一个支持handler的类 *所有的HTTP的方法都会被这个方法掌控。取决于HandlerAdapters 或者handlers 他们自己去决定哪些方法是可用 *@param request current HTTP request *@param response current HTTP response */ protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { /* 当前HTTP请求**/ 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); // 获得HandlerExecutionChain,其包含HandlerIntercrptor和HandlerMethod mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } //获得HandlerAdapter HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); //获得HTTP请求方法 String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (logger.isDebugEnabled()) { logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } //如果有拦截器的话,会执行拦截器的preHandler方法 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } //返回ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } //当view为空时,,根据request设置默认view applyDefaultViewName(processedRequest, mv); //执行拦截器的postHandle mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Error err) { triggerAfterCompletionWithError(processedRequest, response, mappedHandler, 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); } } } }
调用完doDispatch之后就完成了一个请求的访问,其会将渲染后的页面或者数据返回给请求发起者。
springMVC源码分析--DispatcherServlet请求获取及处理的更多相关文章
- springMVC源码分析--访问请求执行ServletInvocableHandlerMethod和InvocableHandlerMethod
在之前一篇博客中springMVC源码分析--RequestMappingHandlerAdapter(五)我们已经简单的介绍到具体请求访问的执行某个Controller中的方法是在RequestMa ...
- springMVC源码分析--RequestToViewNameTranslator请求到视图名称的转换
RequestToViewNameTranslator可以在处理器返回的View为空时使用它根据Request获取viewName.RequestToViewNameTranslator提供的实现类只 ...
- 数据传递--------博客-----------springMVC源码分析--RequestToViewNameTranslator请求到视图名称的转换
参考来源:http://blog.csdn.net/qq924862077/article/details/54286976?utm_source=gold_browser_extension Req ...
- SpringMVC源码分析--容器初始化(五)DispatcherServlet
上一篇博客SpringMVC源码分析--容器初始化(四)FrameworkServlet我们已经了解到了SpringMVC容器的初始化,SpringMVC对容器初始化后会进行一系列的其他属性的初始化操 ...
- SpringMVC源码分析(3)DispatcherServlet的请求处理流程
<springmvc源码分析(2)dispatcherservlet的初始化>初始化DispatcherServlet的多个组件. 本文继续分析DispatcherServlet解析请求的 ...
- springMVC源码分析--AbstractHandlerMethodMapping获取url和HandlerMethod对应关系(十)
在之前的博客springMVC源码分析--AbstractHandlerMapping(二)中我们介绍了AbstractHandlerMethodMapping的父类AbstractHandlerMa ...
- springMVC源码分析--容器初始化(二)DispatcherServlet
在上一篇博客springMVC源码分析--容器初始化(一)中我们介绍了spring web初始化IOC容器的过程,springMVC作为spring项目中的子项目,其可以和spring web容器很好 ...
- 8、SpringMVC源码分析(3):分析ModelAndView的形成过程
首先,我们还是从DispatcherServlet.doDispatch(HttpServletRequest request, HttpServletResponse response) throw ...
- 7、SpringMVC源码分析(2):分析HandlerAdapter.handle方法,了解handler方法的调用细节以及@ModelAttribute注解
从上一篇 SpringMVC源码分析(1) 中我们了解到在DispatcherServlet.doDispatch方法中会通过 mv = ha.handle(processedRequest, res ...
随机推荐
- python数据挖掘_Json结构分析
json是一种轻量级的数据交换格式,也可以说是一种配置文件的格式 这种格式的文件是我们在数据处理经常会遇到的 python提供内置的模块json,只需要在使用前导入即可 你可以通过帮助函数查看json ...
- [HNOI 2010]Bus 公交线路
Description 题库链接 有 \(N\) 个车站, \(K\) 条公交线路.第 \(1\) 到 \(K\) 站是这 \(K\) 线路的起点站.第 \(N-K+1\) 到 \(N\) 是终点站. ...
- [AHOI 2016初中组]迷宫
Description 小雪和小可可被困在了一个无限大的迷宫中. 已经知道这个迷宫有 N 堵环状的墙,如果把整个迷宫看作是一个二维平面,那么每一堵墙都是平面上一个圆.任意两个圆不相交,不重合,也不会相 ...
- hdu 3247 AC自动+状压dp+bfs处理
Resource Archiver Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 100000/100000 K (Java/Ot ...
- poj1753 高斯消元
Flip Game Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 37055 Accepted: 16125 Descr ...
- cmake 没有那个目录
问题:bash: /usr/bin/cmake: 没有那个文件或目录 因为直接使用cmake系统回到默认的/usr/bin中去寻找,但是src中安装的cmake是在/usr/local/bin中,所以 ...
- 如何让Mac、Windows可以互相远程
您可以通过Mac来远程Windows桌面:也可通过Windows来远程Mac界面:甚至还可以通过iOS或Android来远程Mac或Windows. Windows的操作方法,以Windows XP ...
- Spring cloud 学习资料整理
推荐博客 纯洁的微笑 程序猿DD liaokailin的专栏 周立 Spring Cloud 方志朋 Spring Cloud 专栏 许进 跟我学Spring Cloud 推荐网站 Spring Cl ...
- git提交项目常用命令及git分支的用法
1.第一步首先从git托管平台clone项目,我这里就使用idea为例: 填写git的url与存放本地目录名及项目名 2.如果你对项目进行了一些修改,就可以执行git命令,进行提交. 有两种方 ...
- MAX(字段)加0与不加0的测试
--max(字段名)中的"字段名"的数据类型是字符型的,"字段名"+ 0后,oracle会隐式的转换成数字型 --测试 )); insert into Test ...