关联博客:
SpringMVC源码分析之策略对象初始化SpringMVC运行流程分析之前置流程SpringMVC运行流程分析之核心流程 前面SpringMVC运行流程分析之前置流程
博文中我们分析了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最后返回给前台一个解析后的页面。
我们下面看下其核心属性和构造方法。
// 视图实例或者视图名称
private Object view;
// 存放数据 map结构
private ModelMap model;
// 响应状态码 可为空
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, Map<String, ?> model) {
this.view = viewName;
if (model != null) {
getModelMap().addAllAttributes(model);
}
}
// 设置view和model
public ModelAndView(View view, 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( String viewName, Map<String, ?> model, 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, 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,
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, 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, ModelAndView mv) throws Exception {
if (mv != null && !mv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
mv.setViewName(defaultViewName);
}
}
}
// 获取默认视图名称方法如下
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,
HandlerExecutionChain mappedHandler, ModelAndView mv,
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。
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
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,
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实例。
protected View resolveViewName(String viewName, 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;
}