06.SpringMVC 请求处理 - DispatcherServlet
2017-03-02 09:14
363 查看
基本概念
DispatcherServlet 是整个 SpringMVC 的核心,请求处理的具体细节在该类的 doService 方法中定义。原理分析
下面来看该类的 doService 方法:// 在 include 请求之后清除请求属性 private boolean cleanupAfterInclude = true; protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { // 省略部分代码... Map<String, Object> attributesSnapshot = null; // 判断是不是 include 请求 // 即判断 reqeust 中是否含有以 org.springframework.web.servlet 开头的属性 if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap<String, Object>(); // 1.保存请求快照 // 具体指保存 request 中与 SpringMVC 相关的属性到 SnapShot(快照) 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)); } } } // 2.添加到相关属性到请求 // 包括 SpringMVC 容器、LocaleResolver、ThemeResolver、ThemeSource 等 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()); // 由于这里默认是 SessionFlashMapManager 实现类 // 因此表示从 Session 中获取保存的 FlashMap,并将其添加到 request 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); try { // 关键 -> 3.真正的请求处理 doDispatch(request, response); }finally { // 判断异步请求是否在正进行 if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // 4.恢复请求快照 // 具体指将 reqeust 中 SpringMVC 属性恢复成保存 SnapShot 时的样子 if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } } }
分析代码,可以知道该方法的具体实现过程如下:
保存请求快照
添加到相关属性到请求‘’
doDispatch
恢复请求快照
流程探究
1.保存请求快照
当当前 reqeust 为 include reqeust 会保存 SnapShot(请求快照)。include rqeust,具体值的是当前 reqeust 含有 org.springframework.web.servlet 开头的属性。即与 SpringMVC 相关的属性。
SnapShot,本质是一个 Map ,保存了以 org.springframework.web.servlet 开头的属性名称、值。作用是记录当前 reqeust 中与 SpringMVC 相关的属性,以便在以后有需要的时候恢复当前的属性名称、值。
2.添加到相关属性到请求
指将 SpringMVC 容器,以及 SpringMVC 相关的组件,例如 LocaleResolver、ThemeResolver、ThemeSource 等添加到 reqeust 的属性中。方便从 request 中访问它们。3.doDispatch
该过程是负责请求的真正处理。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 { // 1.检测当前 reqeust 是否是 MultipartResolver // 若是则由 MultipartResolver 解析后再返回 processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // 2.取得 HandlerExecutionChain。它包含了处理器和拦截器 mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // 3.取得 HandlerAdapter HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 4.处理请求头的 last-modified 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; } } // 拦截器预处理 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 取得 ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } // 设置 ModelAndView 的 ViewName applyDefaultViewName(processedRequest, mv); // 拦截器后处理 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); } } } }
4.恢复请求快照
该过程表示恢复保存 SnapShot(快照)时的样子。下面来看 restoreAttributesAfterInclude 方法:private void restoreAttributesAfterInclude(HttpServletRequest request, Map<?,?> attributesSnapshot) { // 将当前 request 的与 SpringMVC 相关的属性名称添加到 attrsToCheck Set<String> attrsToCheck = new HashSet<String>(); Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) { attrsToCheck.add(attrName); } } // 添加快照中保存的所有属性名称到 attrsToCheck attrsToCheck.addAll((Set<String>) attributesSnapshot.keySet()); for (String attrName : attrsToCheck) { // 将属性值与快照中的值做比对 Object attrValue = attributesSnapshot.get(attrName); if (attrValue == null){ // 保存快照时属性值为空,则从当前 reqeust 移除该属性 request.removeAttribute(attrName); }else if (attrValue != request.getAttribute(attrName)) { // 保存快照时属性值与当前 reqeust 不同,则恢复 request.setAttribute(attrName, attrValue); } } }
相关文章推荐
- springMVC web请求处理流程
- Ajax响应中文乱码 [SpringMVC使用@ResponseBody处理Ajax请求]
- Ajax响应中文乱码 [SpringMVC使用@ResponseBody处理Ajax请求]
- SpringMVC请求处理流程
- SpringMVC系列(一)核心:处理请求流程
- SpringMVC Spring3 MVC 注解,注释 用@RequestMapping处理请求,多个请求,提交,.do,带参数,url重写
- SpringMVC系列(一)核心:处理请求流程
- Ajax响应中文乱码 [SpringMVC使用@ResponseBody处理Ajax请求]
- springMVC处理Ajax请求的一个错误
- Spring源码分析: SpringMVC启动流程与DispatcherServlet请求处理流程
- Ajax响应中文乱码 [SpringMVC使用@ResponseBody处理Ajax请求]
- SpringMVC对异常进行全局处理,并区分对待ajax和普通请求
- SpringMVC处理ajax请求
- springMVC项目异步处理请求的错误Async support must be enabled on a servlet and for all filters involved in async
- 【SpringMVC】根据请求处理资源表述
- SpringMVC请求处理流程(附图)
- 【转】Ajax响应中文乱码 [SpringMVC使用@ResponseBody处理Ajax请求]
- springMVC系列源码之请求处理过程——12
- SpringMVC请求处理操作
- SpringMVC处理请求的流程