DispatcherServlet#doService方法解析
@Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { // 如果该请求是include的请求(请求包含) 那么就把request域中的数据保存一份快照版本 // 等doDispatch结束之后,会把这个快照版本的数据覆盖到新的request里面去 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. // 说得很清楚,把一些常用对象放进请求域 方便Handler里面可以随意获取 request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); //这个是web子容器哦 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); } try { // DispatcherServlet最重要的方法,交给他去分发请求你、找到handler处理等等 doDispatch(request, response); } finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Restore the original attribute snapshot, in case of an include. //如果是include请求 会上上面的数据快照,重新放置到request里面去 if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } } }
DispatcherServlet#doDispatch方法解析
首先根据请求的路径找到HandlerMethod(带有Method反射属性,也就是对应Controller中的方法),
然后匹配路径对应的拦截器,有了HandlerMethod和拦截器构造个HandlerExecutionChain对象。HandlerExecutionChain对象的获取是通过HandlerMapping接口提供的方法中得到。
有了HandlerExecutionChain之后,通过HandlerAdapter对象进行处理得到ModelAndView对象,HandlerMethod内部handle的时候,使用各种HandlerMethodArgumentResolver实现类处理HandlerMethod的参数(非常重要),使用各种HandlerMethodReturnValueHandler实现类处理返回值。 最终返回值被处理成ModelAndView对象,这期间发生的异常会被HandlerExceptionResolver接口实现类进行处理。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { // 此处用processedRequest 需要注意的是:若是处理上传,processedRequest 将和request不再指向同一对象 HttpServletRequest processedRequest = request; // 异常链处理器 HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { //checkMultipart 这个方法很重要,判断是否是上传需求。且看下面的具体分析::: //如果请求是POST请求,并且请求头中的Context-Type是以multipart/开头的就认为是文件上传的请求 processedRequest = checkMultipart(request); // 标记一下:是否是文件上传的request了 multipartRequestParsed = (processedRequest != request); // 找到一个处理器,如果没有找到对应的处理类的话,这里通常会返回404,如果throwExceptionIfNoHandlerFound属性值为true的情况下会抛出异常 mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // 根据实际的handler去找到一个合适的HandlerAdapter,方法详细逻辑同getHandler,因此不再解释 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 如果是GET请求,如果内容没有变化的话,则直接返回 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; } } // 这段代码很有意思:执行处理器连里的拦截器们,具体参阅下面详细: if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 真正执行我们自己书写的controller方法的逻辑。返回一个ModelAndView // 这也是一个很复杂的过程(序列化、数据绑定等等),需要后面专题讲解 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); // 如果异步启动了,这里就先直接返回了,也就不会再执行拦截器PostHandle之类的 if (asyncManager.isConcurrentHandlingStarted()) { return; } //意思是:如果我们没有设置viewName,就采用默认的。否则采用我们自己的 applyDefaultViewName(processedRequest, mv); // 执行所有的拦截器的postHandle方法,并且把mv给他 // 这里有一个小细节:这个时候拦截器是【倒序】执行的 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { // 这两个catcher什么都不做,只是把异常记录下来 dispatchException = ex; } catch (Throwable err) { dispatchException = new NestedServletException("Handler dispatch failed", 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 processing failed", 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); } } } }
checkMultipart
multipartResolver 在上篇博文初始化的时候讲过了,值是有可能为null的哦(如果你没有配置对应的Bean的话)
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException { // 配置了multipartResolver,并且是文件上传的请求 才会继续往下走 if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) { // 如果该请求已经是MultipartHttpServletRequest 那就输出一个日志走人 if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) { logger.debug("日志。。。"); } else if (hasMultipartException(request) ) { // 判断是否有MultipartException 一般没有 logger.debug("Multipart resolution failed for current request before - " + "skipping re-resolution for undisturbed error rendering"); } else { try { // 这里特别注意,不管是哪种multipartResolver的实现,内部都是new了一个新的MultipartHttpServletRequest的实现类,所以不再指向原来的request了,所以一定要注意 return this.multipartResolver.resolveMultipart(request); } catch (MultipartException ex) { if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) { logger.debug("Multipart resolution failed for error dispatch", ex); // Keep processing error dispatch with regular request handle below } else { throw ex; } } } } // If not returned before: return original request. // 如果前面没有返回,就原样返回,相当于啥都不做 return request; }
这里需要注意的是:org.springframework.web.multipart.support.MultipartFilter,如果在web.xml中配置这个过滤器的话,则会在过滤器中提前判断是不是文件上传的请求,并将请求转换为MultipartHttpServletRequest类型。这个过滤器中默认使用的MultipartResolver为StandardServletMultipartResolver。
在CommonsMultipartResolver中有一个属性叫resolveLazily
private boolean resolveLazily = false;
这个属性值代表是不是延迟解析文件上传,默认为false。最终返回的是一个DefaultMultipartHttpServletRequest的类。这里有一个重要的方法是:parseRequest,这个方法干的事是解析文件上传请求。它的底层是commons-fileupload那一套,不同的是Spring在获取FileItem之后,又进行了一下封装,封装为便于Spring框架整合。tHandler**
@Nullable protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { // 会把配置的所有的HandlerMapping 都拿出来查找,只要找到一个就返回 for (HandlerMapping hm : this.handlerMappings) { if (logger.isTraceEnabled()) { logger.trace( "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'"); } HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } } return null; }