SpringMVC源码剖析-SpringMVC执行流程
Posted 墨家巨子@俏如来
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringMVC源码剖析-SpringMVC执行流程相关的知识,希望对你有一定的参考价值。
前言
上一篇文章写得是SpringMVC组件初始化,这篇文章我们来探讨一下SpringMVC的执行流程
SpringMVC执行流程
SpringMVC执行流程几乎是在面试时面试官对SpringMVC部分的必问之题,下面是SpirngMVC的执行原理图
这个是请求在SpringMVC的执行流程
- DispatcherServlet:请求打过来由DispatcherServlet处理,它是 SpringMVC 中的前端控制器(中央控制器), 负责接收 Request 并将 Request 转发给对应的处理组件
- HandlerMapping:HandlerMapping 维护了 url 和 Controller(Handler)的 映 射关系 。 DispatcherServlet 接 收 请求, 然 后 从 HandlerMapping 查找处理请求的Controller(Handler),标注了@RequestMapping 的每个 method 都可以看成是一个 Handler,HandlerMapping 在请求到达之后, 它的作用便是找到请求相应的处理器 Handler 和 Interceptors。
- HandlerAdapter:SpringMVC通过HandlerAdapter对Handler进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。它的作用就是按照特定的规则去执行 Controller (Handler)
- Handler : Controller (Handler)负责处理请求,Controller 执行后并返回 ModelAndView 对象,其中包括了数据模型和逻辑视图,ModelAndView 是封装结果 视图的组件。Handler把结果返回给HandlerAdapter,HandlerAdapter把结果返回给DispatcherServlet前端控制器。
- ViewResolver:DispatcherServlet收到ModelAndView,调用视图解析器(ViewResolver)来解析HandlerAdapter传递的ModelAndView。Handler执行完成后返回的是逻辑视图,也就是视图名字,一个String ,还有一个Model就是数据模型,封装成ModelAndView。ViewResolver视图解析的作用就是根据视图名,把本地模板文件(比如:xx.jsp;xx.ftl)解析为View视图对象。View用来渲染视图,也就是负责把Handler返回的数据模型model,填充到模板(jsp;ftl)形成html格式的静态内容。
- 最后就是把生成的html通过response写给浏览器,浏览器进行html渲染展示。
核心类认识
开始之前先来认识几个对象,否则下面会晕菜
HandlerMethod :是的controller中方法的封装,其中包括在Controller的Bean对象以及Method方法对象
public class HandlerMethod
/** Logger that is available to subclasses */
protected final Log logger = LogFactory.getLog(HandlerMethod.class);
private final Object bean; //controller对应的bean
private final Method method; //方法对象
private final BeanFactory beanFactory;
private final MethodParameter[] parameters;
private final Method bridgedMethod;
MappedInterceptor :对拦截器的封装
public final class MappedInterceptor
private final String[] includePatterns;
private final String[] excludePatterns; //排除
private final HandlerInterceptor interceptor; //拦截器
RequestMappingInfo : 对@Controller和@RequestMapping注解的类的方法进行解析,然后把解析的结果封装成RequestMappingInfo , 对象中包含了针对@RequestMapping注解的6个属性的匹配条件,DispaterServlet需要根据找到请求匹配的Handler就需要满足6个匹配条件。
- PatternsRequestCondition :模式请求路径过滤器 ,根据request请求路径进行匹配
- RequestMethodsRequestCondition : 请求方法过滤器,根据request请求方式进行匹配
- ParamsRequestCondition :请求参数过滤器,根据请求的参数进行匹配
- HeadersRequestCondition : 请求过滤器 ,根据请求头进行匹配
- ConsumesRequestCondition :根据 筛选出同请求Accpet匹配的媒体类型表达式列表
- ProducesRequestCondition :应答媒体类型过滤器,根据请求Content-Type媒体类型进行匹配
- RequestCondition (optional, custom request condition)
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo>
//根据url进行匹配
private final PatternsRequestCondition patternsCondition;
//方法匹配条件
private final RequestMethodsRequestCondition methodsCondition;
//请求参数过滤器,参数匹配条件
private final ParamsRequestCondition paramsCondition;
//头字段过滤器,请求头匹配条件
private final HeadersRequestCondition headersCondition;
//Consumes匹配条件
private final ConsumesRequestCondition consumesCondition;
// Produces匹配条件
private final ProducesRequestCondition producesCondition;
DispatchServlet执行流程
DispatchServlet是一个Servlet,请求进来会执行 DispatcherServlet#doService 方法
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception
...省略...
// Make framework objects available to handlers and view objects.
//把容器对象设置到request
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
//把localeResolver设置给request
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
//把themeResolver设置给request
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));
//设置flashMapManager
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
try
//执行请求分发
doDispatch(request, response);
finally
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted())
return;
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null)
restoreAttributesAfterInclude(request, attributesSnapshot);
DispatcherServlet#doService调用了DispatcherServlet#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.检查是否是Multipart文件上传
processedRequest = checkMultipart(request);
multipartRequestParsed = processedRequest != request;
// Determine handler for the current request.
//2.遍历所有 handlerMappings 找到当前请求对应的handler,
//比如:RequestMappingHandlerMapping 中维护了一个LinkedHashMap类型的handlerMethods,
//是对HandlerMethod的存储。底层根据URL从handlerMethods匹配一个HandlerMethod,
//然后把HandlerMethod封装到一个HandlerExecutionChain中
//HandlerMethod是对controller方法method的封装,
//这里拿到的是HandlerExecutionChain handler执行链,其中封装了Handler和inteceptor
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null)
//如果没找到会走404
noHandlerFound(processedRequest, response);
return;
// Determine handler adapter for the current request.
//3.从handlerAdapters中循环,找到支持handler的,处理请求适配器HandlerAdapter(如:RequestMappingHandlerAdapter)
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
//处理lastModified
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method))
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled())
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet)
return;
//4.调用拦截器的preHandle
if (!mappedHandler.applyPreHandle(processedRequest, response))
return;
try
// Actually invoke the handler.
//5.调用 HandlerAdapter 实际执行handler,返回ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
finally
if (asyncManager.isConcurrentHandlingStarted())
return;
//6.处理默认的视图名,如果mv != null && !mv.hasView()
//就会使用RequestToViewNameTranslator#getViewName解析一个默认的view
applyDefaultViewName(request, mv);
//7.调用拦截器的postHandle
mappedHandler.applyPostHandle(processedRequest, response, mv);
catch (Exception ex)
dispatchException = ex;
//8.处理结果,内部会使用viewResolver.resolveViewName渲染视图得到View对象
//然后执行:View#render方法渲染
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
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
// Clean up any resources used by a multipart request.
if (multipartRequestParsed)
cleanupMultipart(processedRequest);
这里对核心方法做一个解释
查找HandlerMethod
getHandler 方法:该方法是遍历所有 handlerMappings ,找到合适的HandlerMapping,然后通过HandlerMapping#getHandler查找当前请求对应的HandlerMethod。(HandlerMethod是对controller中方法的封装),这里拿到的是HandlerExecutionChain 执行链而不是HandlerMethod,HandlerExecutionChain中封装了HandlerMethod和inteceptor拦截器;见:org.springframework.web.servlet.DispatcherServlet#getHandler(javax.servlet.http.HttpServletRequest)
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception
for (HandlerMapping hm : this.handlerMappings)
if (logger.isTraceEnabled())
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
//使用HandlerMapping 获取处理请求的Handler,封装到HandlerExecutionChain执行链对象中
//HandlerExecutionChain包括了HandlerMethod和interceptor拦截器
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null)
return handler;
return null;
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception
//找到Handler
Object handler = getHandlerInternal(request);
if (handler == null)
handler = getDefaultHandler();
if (handler == null)
return null;
// Bean name or resolved handler?
if (handler instanceof String)
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
//把handler封装成HandlerExecutionChain ,其中还包括了拦截器的封装
return getHandlerExecutionChain(handler, request);
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request)
//创建一个Handler执行链对象
HandlerExecutionChain chain =
(handler instanceof HandlerExecutionChain) ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler);
//添加拦截器到HandlerExecutionChain
chain.addInterceptors(getAdaptedInterceptors());
String lookupPath = urlPathHelper.getLookupPathForRequest(request);
for (MappedInterceptor mappedInterceptor : mappedInterceptors)
if (mappedInterceptor.matches(lookupPath, pathMatcher))
chain.addInterceptor(mappedInterceptor.getInterceptor());
return chain;
getHandler方法最终会走到org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal ,该方法就是根据request查找一个HandlerMethod
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception
//拿到请求的路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled())
logger.debug("Looking up handler method for path " + lookupPath);
//根据请求路径,找到匹配的HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
if (logger.isDebugEnabled())
if (handlerMethod != null)
logger.debug("Returning handler method [" + handlerMethod + "]");
else
logger.debug("Did not find handler method for [" + lookupPath + "]");
return (handlerMethod != null) ? handlerMethod.createWithResolvedBean() : null;
这里在拿到请求的路径,根据请求路径,找到匹配的HandlerMethod,再往里面走
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception
//matches是requestMppingInfo和HandlerMethod的匹配器集合
List<Match> matches = new ArrayList<Match>();
//根据请求的路径,从 this.urlMap中拿到RequestMappingInfo,
//urlMap 是一个LinkedHashMap,存储了Url路径和RequestMappingInfo的映射关系
List<T> directPathMatches = this.urlMap.get(lookupPath);
if (directPathMatches != null)
addMatchingMappings(directPathMatches, matches, request);
if (matches.isEmpty())
// No choice but to go through all mappings
//如果从urlMap中没有找到RequestMappingInfo,那么matches就是空的
//根据Request对象封装一个requestMppingInfo,然后handlerMethods的keySet中拿到对应的HandlerMethod,(会根据Request中的URL,请求方式,参数params,请求头headers,consumes,produces去匹配handlerMethods.keySet中的requestMppingInfo)
//然后从handlerMethods根据requestMppingInfo拿到HandlerMethod
//把requestMppingInfo和HandlerMethod封装成Match添加到matches中
addMatchingMappings(this.handlerMethods.keySet(), matches, request);
if (!matches.isEmpty())
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator);
if (logger.isTraceEnabled())
logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
Match bestMatch = matches.get(0);
if (matches.size() > 1)
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0)
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
throw new IllegalStateException(
"Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': " +
m1 + ", " + m2 + "");
handleMatch(bestMatch.mapping, lookupPath, request);
//从bestMatch拿到Handler
return bestMatch.handlerMethod;
else
return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
这里出现了两个集合,urlMap 和 handlerMethods ,urlMap 存储的是url和RequestMappingInfo的映射关系,handlerMethods 存储的是RequestMappingInfo和HandlerMethod的关系。
这里的代码比较复杂,先是从 urlMap中根据url拿到RequestMappingInfo集合然后,如果没获取到即:matches.isEmpty()就会调用addMatchingMappings去处理,有兴趣自己去断点一下,该方法的大致流程如下
- 根据request中的url,method,参数params,请求头headers,consumes,produces去创建一个requestMppingInfo
- 然后根据requestMppingInfo为key从handlerMethods拿到HandlerMethod封装成Match 对象添加到matches集合中
- 从Match 中拿到当前请求对应的HandlerMethod
那么这里的handlerMethods和urlMap 是哪儿来的?
以:RequestMappingHandlerMapping为例在其父类AbstractHandlerMethodMapping中维护了一个 Map<T, HandlerMethod> handlerMethods = new LinkedHashMap<T, HandlerMethod>()
该集合中装的就是RequestMappingInfo和HandlerMethod的映射关系,RequestMappingInfo是的RequstMapping的封装,HandlerMethod是对Conroller中方法的封装。初始化流程如下
- AbstractHandlerMethodMapping它实现了InitializingBean接口,在RequestMappingHandlerMapping初始化的使用会触发AbstractHandlerMethodMapping#
afterPropertiesSet
方法 - 该方法中会调用AbstractHandlerMethodMapping#initHandlerMethods 初始化HandlerMethod,
其实就是从容器中拿到所有标识了@Controller或者@RequestMapping
注解的Bean,也就是controller,然后拿到其中的所有method封装成HandlerMethod对象,然后再拿到method上RequestMapping信息封装成RquestMappingInfo对象,再把RquestMappingInfo和HandlerMethod以前者为key,后置为value注册到handlerMethods集合中。 - 把Url路径和RequestMappingInfo的映射关系存储到AbstractHandlerMethodMapping类中的
MultiValueMap<String, T> urlMap = new LinkedMultiValueMap<String, T>()
集合中
这个是在MappingHandlerMapping初始化的时候就把HandlerMethod封装好存储在了handlerMethods 集合中,当请求过来就根据Request去匹配一个RequestMappingInfo,然后再找到对应的HandlerMethod返回。
查找HandlerAdapter
查找HandlerAdapter在org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter中,
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException
for (HandlerAdapter ha : this.handlerAdapters)
if (logger.isTraceEnabled())
logger.trace("Testing handler adapter [" + ha + "]");
if (ha.supports(handler))
return ha;
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
// org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#supports
public final boolean supports<以上是关于SpringMVC源码剖析-SpringMVC执行流程的主要内容,如果未能解决你的问题,请参考以下文章