关联博客:
博文中我们分析了springMVC请求处理的前置流程,本文我们分析其核心流程也就是doDispatch(request, response);
。
这里先放一下时序图展示整体流程:
那么HandlerAdapter核心流程逻辑呢?
【1】整体结构
① 源码概览
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; // 处理器执行链对象 HandlerExecutionChain mappedHandler = null; // 是否multipart请求 boolean multipartRequestParsed = false; // 异步处理管理器 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { //模型视图对象 ModelAndView mv = null; // 异常对象 Exception dispatchException = null; try { // 如果是multipart request 将会尝试使用multipartResolver解析 processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // Determine handler for the current request. 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); // 如果是get且没有修改,直接返回 if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } // 如果某个拦截器前置方法返回false,直接返回 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 核心处理方法 // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); // 如果是异步请求处理,则这里就返回 if (asyncManager.isConcurrentHandlingStarted()) { return; } // 如果MV不为null,但是没有view,则尝试获取defaultViewName 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 other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } // 处理最后结果,这里可能会返回到一个页面或者抛出异常,或者返回json等 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { // 拦截器的afterCompletion方法 triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { // 如果是异步请求 且 mappedHandler 不为null, if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { // 如果拦截器是asyncInterceptor类型,则调用afterConcurrentHandlingStarted方法 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { // 如果是multipartRequest,则清理资源 fielItem哦 cleanupMultipart(processedRequest); } } } }
仅就这段方法来看,其流程如下所示:
这里有三个步骤十分关键,分别是第三步mappedHandler = getHandler(processedRequest);获取执行器链,第六步mv = ha.handle(processedRequest, response, mappedHandler.getHandler());获取MV以及最终的第十步processDispatchResult处理最终结果。
第三步是给请求尝试找到一个handler,比如某个controller某个方法,或者说默认的处理器DefaultServletHttpRequestHandler等。然后实例化HandlerExecutionChain 后添加合适的拦截器。
第六步则是核心的处理过程,会进行参数解析、目标方法的反射调用以及最终返回结果的处理。
第十步processDispatchResult处理最终结果通常比如渲染视图或者返回json。如果在前面处理过程中抛出了异常,那么这里也会对异常尝试处理。
② 核心对象HandlerExecutionChain
HandlerExecutionChain ,处理器执行链。其通过遍历HandlerMapping,根据其方法HandlerExecutionChain getHandler(HttpServletRequest request)获取得到。
通过如下核心属性与构造方法可知,其由handler和HandlerInterceptor组成。handler比如HandlerMethod(或其他handler类)即实际处理请求的目标类(方法)。HandlerInterceptor就是拦截器,在处理流程中分别会触发其preHandle、postHandle以及afterCompletion方法。HandlerExecutionChain 中的handler可能如下:
实现了org.springframework.web.servlet.function.HandlerFunction,比如ResourceHandlerFunction
实现了org.springframework.web.servlet.mvc.Controller,比如ParameterizableViewController
实现了org.springframework.web.HttpRequestHandler,比如DefaultServletHttpRequestHandler
实现了javax.servlet.Servlet,比如ViewStatusMessagesServlet
实现了org.springframework.web.method.HandlerMethod,比如ServletInvocableHandlerMethod,controller里面很多方法就被包装为了该类型。这个也是最常见的核心属性和构造方法:
private final Object handler; private final List<HandlerInterceptor> interceptorList = new ArrayList<>(); private int interceptorIndex = -1; /** * Create a new HandlerExecutionChain. * @param handler the handler object to execute */ public HandlerExecutionChain(Object handler) { this(handler, (HandlerInterceptor[]) null); }
③ 核心对象ModelAndView
模型视图对象,模型这里理解为一个存放数据的map,视图view通常需要一个ViewResolver
解析处理。视图解析器会获取到model然后渲染view最后返回给前台一个解析后的页面。
我们下面看下其核心属性和构造方法。
// 视图实例或者视图名称 @Nullable private Object view; // 存放数据 map结构 @Nullable private ModelMap model; // 响应状态码 可为空 @Nullable private HttpStatus status; // 指示是否已经调用clear方法进行清理 private boolean cleared = false; // 空构造方法 public ModelAndView() { } // 设置viewName public ModelAndView(String viewName) { this.view = viewName; } // 设置view public ModelAndView(View view) { this.view = view; } // 设置viewName和model public ModelAndView(String viewName, @Nullable Map<String, ?> model) { this.view = viewName; if (model != null) { getModelMap().addAllAttributes(model); } } // 设置view和model public ModelAndView(View view, @Nullable Map<String, ?> model) { this.view = view; if (model != null) { getModelMap().addAllAttributes(model); } } // 设置viewName status public ModelAndView(String viewName, HttpStatus status) { this.view = viewName; this.status = status; } // 完整实例 public ModelAndView(@Nullable String viewName, @Nullable Map<String, ?> model, @Nullable HttpStatus status) { this.view = viewName; if (model != null) { getModelMap().addAllAttributes(model); } this.status = status; } // 设置viewName 然后向model中添加属性 modelName = modelObject public ModelAndView(String viewName, String modelName, Object modelObject) { this.view = viewName; addObject(modelName, modelObject); } // 设置view然后向model中添加属性 modelName = modelObject public ModelAndView(View view, String modelName, Object modelObject) { this.view = view; addObject(modelName, modelObject); }
另外,关于HandlerMapping组件、HandlerAdapter组件可以参考下面博文,这里不再赘述。
SpringMVC常见组件之HandlerMapping分析、SpringMVC常见组件之HandlerAdapter分析
【2】方法说明
① checkMultipart
源码如下所示,其核心是判断当前请求是否为multipart request
并尝试使用multipartResolver
进行解析。通常这一步应用于文件上传场景。
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException { if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) { if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) { if (request.getDispatcherType().equals(DispatcherType.REQUEST)) { logger.trace("Request already resolved to MultipartHttpServletRequest, e.g. by MultipartFilter"); } } else if (hasMultipartException(request)) { logger.debug("Multipart resolution previously failed for current request - " + "skipping re-resolution for undisturbed error rendering"); } else { try { 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; }
② getHandler(processedRequest)
获取处理器执行链对象HandlerExecutionChain
。这里本质是遍历容器中的HandlerMapping
,调用其getHandler方法获取HandlerExecutionChain 。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; }
关于mapping.getHandler(request)具体逻辑参考博文SpringMVC常见组件之HandlerMapping分析。
③ getHandlerAdapter(mappedHandler.getHandler())
同②一样,这里会遍历容器中的handlerAdapters,找到一个支持当前handler的适配器。如果找不到,将会抛出ServletException异常。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { if (this.handlerAdapters != null) { for (HandlerAdapter adapter : this.handlerAdapters) { if (adapter.supports(handler)) { return adapter; } } } throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); }
④ 拦截器的方法
这里指的是HandlerExecutionChain.applyPreHandle、HandlerExecutionChain.applyPostHandle
以及triggerAfterCompletion(有两个哦)
。
① 前置处理与后置处理
applyPreHandle与applyPostHandle本质只是遍历interceptorList,调用其preHandle方法。
需要注意的是在applyPreHandle中,如果某个拦截器的preHandle方法返回false,则会触发triggerAfterCompletion方法。applyPostHandle则不会触发triggerAfterCompletion方法。
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { for (int i = 0; i < this.interceptorList.size(); i++) { HandlerInterceptor interceptor = this.interceptorList.get(i); if (!interceptor.preHandle(request, response, this.handler)) { // 这里第三个参数为null triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } return true; } void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { for (int i = this.interceptorList.size() - 1; i >= 0; i--) { HandlerInterceptor interceptor = this.interceptorList.get(i); interceptor.postHandle(request, response, this.handler, mv); } }
②triggerAfterCompletion
首先是DispatcherServlet
的triggerAfterCompletion
,其调用位置如下图所示(会将异常跑出去):
方法如下所示,其会调用mappedHandler(HandlerExecutionChain)
的triggerAfterCompletion
方法,然后抛出异常。
private void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, Exception ex) throws Exception { if (mappedHandler != null) { mappedHandler.triggerAfterCompletion(request, response, ex); } throw ex; }
HandlerExecutionChain
的triggerAfterCompletion
方法如下所示,本质是遍历interceptorList
调用每一个拦截器的afterCompletion
方法。
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) { for (int i = this.interceptorIndex; i >= 0; i--) { HandlerInterceptor interceptor = this.interceptorList.get(i); try { interceptor.afterCompletion(request, response, this.handler, ex); } catch (Throwable ex2) { logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); } } }
从这里也可以看出HandlerExecutionChain
虽然维护了handler
与interceptorList
,但是本身没有业务逻辑,具体业务逻辑处理都委派给了HandlerInterceptor
。
⑤ 默认视图名称处理
如果ha.handle(processedRequest, response, mappedHandler.getHandler())
返回的MV不是null,但是却没有view。那么这里会尝试根据request获取一个默认viewName。
private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception { if (mv != null && !mv.hasView()) { String defaultViewName = getDefaultViewName(request); if (defaultViewName != null) { mv.setViewName(defaultViewName); } } } // 获取默认视图名称方法如下 @Nullable protected String getDefaultViewName(HttpServletRequest request) throws Exception { return (this.viewNameTranslator != null ? this.viewNameTranslator.getViewName(request) : null); }
这里根据请求获取默认视图名称步骤如下:
①获取请求路径 lookupPath
② 如果是以 / 开头,则去掉头部的 /;
③ 如果是以 / 结尾,则去掉尾部的 /;
④ 去掉文件拓展名e.g. "mypath/myfile.txt" -> "mypath/myfile".
⑤ 如果SLASH不等于separator,则进行替换path = StringUtils.replace(path, SLASH, this.separator);,默认情况下SLASH==separator==/
假设这里请求是http://localhost:8080/testNoView,那么获取的lookupPath为 /testNoView,得到的默认视图名称是testNoView。如果目标位置不存在这个文件,则会抛出如下异常:
【3】结果处理
这里指的是processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
。
① processDispatchResult
代码如下所示,分为这样几步:异常处理、视图处理、异步请求判断以及最后的triggerAfterCompletion。
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); } } // 视图处理 if (mv != null && !mv.wasCleared()) { //核心步骤--视图渲染 render(mv, request, response); if (errorView) { //如果是错误视图,清理request中error相关属性 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; } // triggerAfterCompletion 处理 这里不再赘述 if (mappedHandler != null) { // Exception (if any) is already handled.. mappedHandler.triggerAfterCompletion(request, response, null); } }
② 异常处理
如下所示是doDispatch的核心逻辑 。
//doDispatch的核心逻辑 DispatcherServlet#doDispatch - processedRequest = checkMultipart(request); - mappedHandler = getHandler(processedRequest); - HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); - mappedHandler.applyPreHandle(processedRequest, response) - mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); - applyDefaultViewName(processedRequest, mv); - mappedHandler.applyPostHandle(processedRequest, response, mv); - processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); - triggerAfterCompletion(processedRequest, response, mappedHandler, ex); -
如果我们的业务抛出了异常,那么是在什么位置呢?
mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); - AbstractHandlerMethodAdapter#handle - RequestMappingHandlerAdapter#handleInternal - mav = invokeHandlerMethod(request, response, handlerMethod); - RequestMappingHandlerAdapter#invokeHandlerMethod // 假设这里抛出异常--通常就是我们的业务异常 - invocableMethod.invokeAndHandle(webRequest, mavContainer); - return getModelAndView(mavContainer, modelFactory, webRequest);
如下图所示,当执行invokeAndHandle反射调用我们的目标方法时,假设业务抛出了异常,那么这里会一路抛出到DispatcherServlet.doDispatch中,交给processDispatchResult去处理。无论RequestMappingHandlerAdapter还是ServletInvocableHandlerMethod都没有对这里的异常进行拦截。
如下代码所示,如果异常是ModelAndViewDefiningException,则直接获取其ModelAndView属性。否则获取handler,然后根据processHandlerException(request, response, handler, exception)尝试获取MV。
processHandlerException放入如下所示,首先从request移除属性HandlerMapping.class.getName() + ".producibleMediaTypes";然后尝试用HandlerExceptionResolver 处理异常获取到exMV。获取到exMV后如果其不为null,那么将会是否有数据,是否有view;最后暴露错误属性信息给request。
@Nullable protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception { // Success and error responses may use different content types request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); // 遍历异常解析器处理异常,核心步骤 ModelAndView exMv = null; if (this.handlerExceptionResolvers != null) { for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) { exMv = resolver.resolveException(request, response, handler, ex); if (exMv != null) { break; } } } if (exMv != null) { // 判断model是否为空 if (exMv.isEmpty()) { request.setAttribute(EXCEPTION_ATTRIBUTE, ex); return null; } // We might still need view name translation for a plain error model... if (!exMv.hasView()) { // 获取默认视图名称 String defaultViewName = getDefaultViewName(request); if (defaultViewName != null) { exMv.setViewName(defaultViewName); } } if (logger.isTraceEnabled()) { logger.trace("Using resolved error view: " + exMv, ex); } else if (logger.isDebugEnabled()) { logger.debug("Using resolved error view: " + exMv); } // 暴露错误相关属性信息给request WebUtils.exposeErrorRequestAttributes(request, ex, getServletName()); return exMv; } throw ex; }
如果我们使用@ControllerAdvice和@ExceptionHandler(value = {Exception.class})来捕捉异常进行全局异常处理。那么上面exMv = resolver.resolveException(request, response, handler, ex);我们会调用异常解析器去处理异常,得到的exMV呢是一个空的ModelAndView。
关于异常解析器处理异常逻辑参考博文SpringMVC常见组件之HandlerExceptionResolver分析。这里我们看下WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());方法。
public static void exposeErrorRequestAttributes(HttpServletRequest request, Throwable ex, @Nullable String servletName) { exposeRequestAttributeIfNotPresent(request, ERROR_STATUS_CODE_ATTRIBUTE, HttpServletResponse.SC_OK); exposeRequestAttributeIfNotPresent(request, ERROR_EXCEPTION_TYPE_ATTRIBUTE, ex.getClass()); exposeRequestAttributeIfNotPresent(request, ERROR_MESSAGE_ATTRIBUTE, ex.getMessage()); exposeRequestAttributeIfNotPresent(request, ERROR_EXCEPTION_ATTRIBUTE, ex); exposeRequestAttributeIfNotPresent(request, ERROR_REQUEST_URI_ATTRIBUTE, request.getRequestURI()); if (servletName != null) { exposeRequestAttributeIfNotPresent(request, ERROR_SERVLET_NAME_ATTRIBUTE, servletName); } }
错误属性key如下所示(都是public static final
):
String EXCEPTION_ATTRIBUTE = DispatcherServlet.class.getName() + ".EXCEPTION"; String ERROR_STATUS_CODE_ATTRIBUTE = "javax.servlet.error.status_code"; String ERROR_EXCEPTION_TYPE_ATTRIBUTE = "javax.servlet.error.exception_type"; String ERROR_MESSAGE_ATTRIBUTE = "javax.servlet.error.message"; String ERROR_EXCEPTION_ATTRIBUTE = "javax.servlet.error.exception"; String ERROR_REQUEST_URI_ATTRIBUTE = "javax.servlet.error.request_uri"; String ERROR_SERVLET_NAME_ATTRIBUTE = "javax.servlet.error.servlet_name";
③视图处理
视图处理这里指的是render(mv, request, response);
。ModelAndView里面包含了数据和视图信息,那么这一部分通常主要逻辑就是数据渲染,给浏览器一个解析好的页面。
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { // 尝试解析获取得到locale信息 Locale locale = (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale()); //为响应设置locale信息 response.setLocale(locale); View view; // 获取视图名称 String viewName = mv.getViewName(); // 如果视图名称不为null,则解析得到View实例 if (viewName != null) { //核心步骤哦,解析viewName,会遍历视图解析器处理 view = resolveViewName(viewName, mv.getModelInternal(), locale, request); if (view == null) { throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + getServletName() + "'"); } } else { // 如果不存在viewName则尝试获取view实例,也不存在,则抛出异常 view = mv.getView(); if (view == null) { throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " + "View object in servlet with name '" + getServletName() + "'"); } } // Delegate to the View object for rendering. if (logger.isTraceEnabled()) { logger.trace("Rendering view [" + view + "] "); } try { // 如果MV中有http status ,则给response设置status状态码 if (mv.getStatus() != null) { response.setStatus(mv.getStatus().value()); } // 视图渲染,核心步骤,调用具体的View实例的render方法 view.render(mv.getModelInternal(), request, response); } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug("Error rendering view [" + view + "]", ex); } throw ex; } }
这里有两步需要注意:resolveViewName(viewName, mv.getModelInternal(), locale, request);解析视图名称得到具体的View以及view.render(mv.getModelInternal(), request, response);调用具体的View实例的render方法进行视图渲染。
如下所示resolveViewName方法解析视图名称得到实际的View实例,核心是遍历容器中的viewResolvers调用其resolveViewName方法得到View实例。
@Nullable protected View resolveViewName(String viewName, @Nullable Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception { if (this.viewResolvers != null) { for (ViewResolver viewResolver : this.viewResolvers) { View view = viewResolver.resolveViewName(viewName, locale); if (view != null) { return view; } } } return null; }