首先看下面这种张图,这张图说明了spring mvc整体的流程。

本文讲的就是如何从DispatcherServlet中得到ModerAndView的过程。

首先看DispatherServlet这个类的doService方法,学过servlet的人都知道,它是web容器处理请求的入口。

 protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
} // Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<String, Object>();
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));
}
}
} // Make framework objects available to handlers and view objects.
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()); 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 {
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);
}
}
}

可以看到,doService做的事情一共就两件,一是保存request的Attribute并在框架处理完之后恢复回去,以防止attribute在框架处理的过程中被迫坏;第二件事就是在第36行调用doDispatch,进入真正的spring mvc流程中。我们来看doDispatch函数,这个函数是DispatchServlet控制整个spring mvc流程的核心,文章开始的那张图所描述的流程都是在这个函数中完成的。我们来看一下这个函数。

 /**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
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 {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request); // Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == 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);
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;
} try {
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
} applyDefaultViewName(request, 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
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}

这个方法的核心工作内容包括这么几件:处理http请求得到模型和视图(存在一个ModelAndView对象中)、处理拦截器、渲染视图、卸载multipart内容,以及获取用于处理模型所需的处理器HanderExecutionChain和适配器HandlerAdapter。

http的请求具体会被怎么处理将取决于处理器执行器HanderExecutionChain和适配器HandlerAdapter,所以我们先看这两个对象是怎么得到的。方法注释的已经大致说明了这两个对象是如何得到的:HanderExecutionChain是通过应用servlet所拥有的HanderMapping生成的,HandlerAdapter是通过依次查询servlet所拥有的适配器中能够支持执行器的适配器得到的。

那么我们就来看一下HanderExecutionChain和HandlerAdapter到底是怎么得到的。

看28行的getHandler,HanderExecutionChain是从这里获取的,跟进去看:

 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() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}

看第7行,真的是通过DistpacherServlet的HandlerMappings得到的,跟进去看怎么得到的:

发现这个servlet拥有的一个HM是AbstractHanderMethondMapping对象,跟进他的方法,发现它在第3行获取了一个HandlerMethod(名为handler的object对象)。

 @Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
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);
}
return getHandlerExecutionChain(handler, request);
}

这个HandlerMethod对象存的是什么呢?我们看看:

原来是它封装了我们映射到的控制器,包括bean,以及map到的方法。有了这个封装对象,剩下的事情就好办了。

继续往下看便到了第15行,跟进去getHandlerExecutionChain方法:

 protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
chain.addInterceptors(getAdaptedInterceptors()); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) {
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
} return chain;
}

可以看到,这个方法new了HandlerExecutionChain对象,并且把HM的拦截器和用户的拦截去都加进去了。至此HandlerExecutionChain的获取过程就讲完了。

接下来看适配器HandlerAdapter是怎么来的。

     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");
}

真的就是看servlet有那些适配器,然后一个个查询是否支持,最后返回。

HanderExecutionChain和HandlerAdapter都有了,那么接下来就要看它们是怎么获取到模型和视图了。

跟进去dispatch方法的56行,一路下去,来到了 ModelAndView invokeHandleMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod)  方法,这个方法是HandlerAdapter适配器的方法啊, 来看看这个方法:

 private ModelAndView invokeHandleMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest(request, response); WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory); ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout); final WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors); if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult(); if (logger.isDebugEnabled()) {
logger.debug("Found concurrent result value [" + result + "]");
}
requestMappingMethod = requestMappingMethod.wrapConcurrentResult(result);
} requestMappingMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) {
return null;
} return getModelAndView(mavContainer, modelFactory, webRequest);
}

终于知道为什么需要适配器了,原来这个适配器的方法中,就适配了spring mvc和servlet,还有几个适配对象,其中最重要的是ServletWebRequest webRequest ,它适配了request。另外还有ModelAndViewContainer mavContainer,通过它持有模型和视图。

另外看这三行,这三行采用了工厂模式。

第一行获取了数据绑定的工厂,最重要的是它有handlerMethod。然后它会传给第三行,得到ServletInvocableHandlerMethod requestMappingMethod 。这个 requestMappingMethod 就是我们进入控制器调用的关键。

第二行获取了模型工厂,有了它,就可以创建模型了。

值得注意的是,这三个对象,每个里面都有handlerMethod的信息。

WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);

看第35行,requestMappingMethod.invokeAndHandle(webRequest, mavContainer),这里开始真正的填充模型了,怎么填充呢?其实猜都能猜到,首先它自己有handlermethod,又传入了webRequest和mavContainer,很容易想到,它肯定是根据请求,通过反射获取控制器的requestMap方法,将参数传入并调用方法,最后得到控制器处理后的模型,并得到控制器指定的视图。至于传入的参数是如何得到的,我在另一篇文章李有比较详细的描述,请看spring mvc中的控制器方法中的参数从哪里传进来这篇文章。

看起来貌似已经把整个流程讲完了,但是等等,有个很重要的问题,生成处理器执行器HandlerExecutionChain的时候需要从HM里get到一个真正的处理器传入给HandlerExecutionChain的构造器。这个处理器怎么来的?这个带着这个疑问,我们继续来看他们是怎么得到的。

前面已经说了,这个handler是在HandlerExecutionChain getHandler(HttpServletRequest request) 的第三行调用HM的Object handler = getHandlerInternal(request)得到的,我们看一看getHandlerInternal这个函数:

 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 = 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);
}

可以看到,getHandlerInternal是通过请求的路径来查询得到那个关键的处理器(在这里是一个HandlerMethod,它含有控制器的requestMap方法签名),再看一下第6行,它是怎么通过路径查询到处理器的,跟进去lookupHandlerMethod函数:

 protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
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...
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);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
}
}

lookupHandlerMethod函数把获取的处理器包装成一个个Match,并对Match进行了排序(因为一个path可能不仅仅对应一个处理器,比如存在两个requestMap路径相同的函数,那么一个path就会对应两个处理器)之后取最匹配的那个。在这个过程中甚至还做了重复性检测,如果有两个一模一样的处理器存在,那说明我们的控制器写的有歧义了,直接抛出异常。我们看到第5行 addMatchingMappings(directPathMatches, matches, request); ,这里是真正获取HandlerMethod的地方,跟进去看:

     private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
for (T mapping : mappings) {
T match = getMatchingMapping(mapping, request);
if (match != null) {
matches.add(new Match(match, this.handlerMethods.get(mapping)));
}
}
}

直接看到第5行 matches.add(new Match(match, this.handlerMethods.get(mapping))) ,这里传入了一个重要的this.handlerMethods.get(mapping),this.handlerMethods 是一个LinkedHashMap,存放了此HM所有的处理器,根据mapping对象进行索引(mapping对象标识了唯一的请求映射),看一看这个handlerMethods 里有啥:

果然我们写的控制器方法对应的handler,都在这里头保存着。

接下来我们看看,这些handler是怎么保存到HM中的。我们在HM中找到了一个initHandlerMethods方法,从名字中就可以看出来这个方法是初始化methods用的,我们看看这个方法的内容:

 protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
} String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class)); for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) &&
isHandler(getApplicationContext().getType(beanName))){
detectHandlerMethods(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}

它从ApplicationContext中获取了bean的名字,然后根据名字,在第13行中通过 detectHandlerMethods(beanName) 得到真正的bean:

 protected void detectHandlerMethods(final Object handler) {
Class<?> handlerType =
(handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass()); // Avoid repeated calls to getMappingForMethod which would rebuild RequestMappingInfo instances
final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
final Class<?> userType = ClassUtils.getUserClass(handlerType); Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
@Override
public boolean matches(Method method) {
T mapping = getMappingForMethod(method, userType);
if (mapping != null) {
mappings.put(method, mapping);
return true;
}
else {
return false;
}
}
}); for (Method method : methods) {
registerHandlerMethod(handler, method, mappings.get(method));
}
}

看第24行,在这里注册HM所持的HandlerMethod,跟进去:

 protected void registerHandlerMethod(Object handler, Method method, T mapping) {
HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);
if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {
throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean() +
"' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '" +
oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");
} this.handlerMethods.put(mapping, newHandlerMethod);
if (logger.isInfoEnabled()) {
logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);
} Set<String> patterns = getMappingPathPatterns(mapping);
for (String pattern : patterns) {
if (!getPathMatcher().isPattern(pattern)) {
this.urlMap.add(pattern, mapping);
}
}
}

看第10行 this.handlerMethods.put(mapping, newHandlerMethod) ,一个个Put 进去了,这样,就完成了HandlerMethod的注册。在第18行 this.urlMap.add(pattern, mapping) ,完成了路径到mapping的映射。

最后只要框架在启动是调用initMethod方法,就可以完成处理器的注册了。

自此,DispatcherServlet如何得到ModelAndView的过程就讲完了。

spring mvc中DispatcherServlet如何得到ModelAndView的的更多相关文章

  1. Spring mvc中DispatcherServlet详解

    简介 DispatcherServlet是前端控制器设计模式的实现,提供SpringWebMVC的集中访问点,而且负责职责的分派,而且与spring IOC容器无缝集成,从而可以获得Spring的优势 ...

  2. Spring mvc 中 DispatcherServlet 的学习和理解

    上图表示当客户请求来到时,spring架构作出响应的流程,可以从图中看到看到请求分发的中心就是 DispatcherServlet 类,DispatcherServlet的任务是将请求发送给Sprin ...

  3. Spring MVC中DispatcherServlet工作原理探究

    转:http://blog.csdn.net/zhouyuqwert/article/details/6853730 下面类图将主要的类及方法抽离出来,以便查看方便,根据类的结构来说明整个请求是如何工 ...

  4. Spring Mvc中DispatcherServlet和Servlet的区别小结

    在web开发过程中开始接触的是servlet,用来处理用户请求.这几年随着spring 框架越来越成熟,几乎成了java web开发界的主流框架.既然这么受欢迎肯定有它的优点,spring框架在原来的 ...

  5. Spring MVC 中的基于注解的 Controller【转】

    原文地址:http://my.oschina.net/abian/blog/128028 终于来到了基于注解的 Spring MVC 了.之前我们所讲到的 handler,需要根据 url 并通过 H ...

  6. Spring MVC中基于注解的 Controller

         终于来到了基于注解的 Spring MVC 了.之前我们所讲到的 handler,需要根据 url 并通过 HandlerMapping 来映射出相应的 handler 并调用相应的方法以响 ...

  7. Spring MVC中的HandlerMapping与HandlerAdapter

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  8. Spring MVC 中的基于注解的 Controller(转载)

           终于来到了基于注解的 Spring MVC 了.之前我们所讲到的 handler,需要根据 url 并通过 HandlerMapping 来映射出相应的 handler 并调用相应的方法 ...

  9. Spring MVC中处理静态资源的多种方法

    处理静态资源,我想这可能是框架搭建完成之后Web开发的”头等大事“了. 因为一个网站的显示肯定会依赖各种资源:脚本.图片等,那么问题来了,如何在页面中请求这些静态资源呢? 还记得Spring MVC中 ...

随机推荐

  1. .net 委托的简化语法

    1. 不需要构造委托对象 ThreadPool.QueueUserWorkItem:通过线程池 public static void WorkItem() { ThreadPool.QueueUser ...

  2. Android-ListView-CursorAdapter

    在上篇博客,Android-ListView-SimpleCursorAdapter,中介绍了SimpleCurosrAdapter的使用操作(SimpleCursorAdapter是简单便捷Curs ...

  3. mysql数据库使用sql查询数据库大小及表大小

    网上查了很多资料,最后发现一个可行的,分享如下: 数据库大小查询: select concat(round(sum(DATA_LENGTH/1024/1024),2),'M') from inform ...

  4. Verilog MIPS32 CPU(一)-- PC寄存器

    Verilog MIPS32 CPU(一)-- PC寄存器 Verilog MIPS32 CPU(二)-- Regfiles Verilog MIPS32 CPU(三)-- ALU Verilog M ...

  5. Djangorestframework编写post接口

    前提:已经有Django环境 一.安装 pip install djangorestframework 二.编写视图 import json from django.shortcuts import ...

  6. HTTP杂项

    HTTP请求头中的origin, referer和host三个字段的区别 host 描述请求将被发送的目的地,包括,且仅仅包括域名和端口号 在任何类型请求中,request都会包含此header信息. ...

  7. mongodb 搭建集群(分片+副本集)

    mongodb  搭建集群(分片+副本集) 一.搭建结构图: 二.搭建步骤:

  8. 浅谈K8S cni和网络方案

    此文已由作者黄扬授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 在早先的k8s版本中,kubelet代码里提供了networkPlugin,networkPlugin是一组接 ...

  9. django admin后台的简单使用

    创建自己的model.py文件 from django.db import models from django.contrib.auth.models import ( BaseUserManage ...

  10. 关于logstash-out-mongodb插件说明

    从kafka获取数据,存到mongodb中.适合空间查询geo_point设置.配置文件如下: input {  kafka {    type => "test"    a ...