spring mvc中DispatcherServlet如何得到ModelAndView的

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

spring mvc中DispatcherServlet如何得到ModelAndView的_第1张图片

 

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

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

 1 protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
 2         if (logger.isDebugEnabled()) {
 3             String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
 4             logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
 5                     " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
 6         }
 7 
 8         // Keep a snapshot of the request attributes in case of an include,
 9         // to be able to restore the original attributes after the include.
10         Map attributesSnapshot = null;
11         if (WebUtils.isIncludeRequest(request)) {
12             attributesSnapshot = new HashMap();
13             Enumeration attrNames = request.getAttributeNames();
14             while (attrNames.hasMoreElements()) {
15                 String attrName = (String) attrNames.nextElement();
16                 if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
17                     attributesSnapshot.put(attrName, request.getAttribute(attrName));
18                 }
19             }
20         }
21 
22         // Make framework objects available to handlers and view objects.
23         request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
24         request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
25         request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
26         request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
27 
28         FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
29         if (inputFlashMap != null) {
30             request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
31         }
32         request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
33         request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
34 
35         try {
36             doDispatch(request, response);
37         }
38         finally {
39             if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
40                 return;
41             }
42             // Restore the original attribute snapshot, in case of an include.
43             if (attributesSnapshot != null) {
44                 restoreAttributesAfterInclude(request, attributesSnapshot);
45             }
46         }
47     }

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

 1 /**
 2      * Process the actual dispatching to the handler.
 3      * 

The handler will be obtained by applying the servlet's HandlerMappings in order. 4 * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters 5 * to find the first that supports the handler class. 6 *

All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers 7 * themselves to decide which methods are acceptable. 8 * @param request current HTTP request 9 * @param response current HTTP response 10 * @throws Exception in case of any kind of processing failure 11 */ 12 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { 13 HttpServletRequest processedRequest = request; 14 HandlerExecutionChain mappedHandler = null; 15 boolean multipartRequestParsed = false; 16 17 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 18 19 try { 20 ModelAndView mv = null; 21 Exception dispatchException = null; 22 23 try { 24 processedRequest = checkMultipart(request); 25 multipartRequestParsed = (processedRequest != request); 26 27 // Determine handler for the current request. 28 mappedHandler = getHandler(processedRequest); 29 if (mappedHandler == null || mappedHandler.getHandler() == null) { 30 noHandlerFound(processedRequest, response); 31 return; 32 } 33 34 // Determine handler adapter for the current request. 35 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); 36 37 // Process last-modified header, if supported by the handler. 38 String method = request.getMethod(); 39 boolean isGet = "GET".equals(method); 40 if (isGet || "HEAD".equals(method)) { 41 long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); 42 if (logger.isDebugEnabled()) { 43 logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); 44 } 45 if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { 46 return; 47 } 48 } 49 50 if (!mappedHandler.applyPreHandle(processedRequest, response)) { 51 return; 52 } 53 54 try { 55 // Actually invoke the handler. 56 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 57 } 58 finally { 59 if (asyncManager.isConcurrentHandlingStarted()) { 60 return; 61 } 62 } 63 64 applyDefaultViewName(request, mv); 65 mappedHandler.applyPostHandle(processedRequest, response, mv); 66 } 67 catch (Exception ex) { 68 dispatchException = ex; 69 } 70 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 71 } 72 catch (Exception ex) { 73 triggerAfterCompletion(processedRequest, response, mappedHandler, ex); 74 } 75 catch (Error err) { 76 triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err); 77 } 78 finally { 79 if (asyncManager.isConcurrentHandlingStarted()) { 80 // Instead of postHandle and afterCompletion 81 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); 82 return; 83 } 84 // Clean up any resources used by a multipart request. 85 if (multipartRequestParsed) { 86 cleanupMultipart(processedRequest); 87 } 88 } 89 }

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

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

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

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

 1 protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
 2         for (HandlerMapping hm : this.handlerMappings) {
 3             if (logger.isTraceEnabled()) {
 4                 logger.trace(
 5                         "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
 6             }
 7             HandlerExecutionChain handler = hm.getHandler(request);
 8             if (handler != null) {
 9                 return handler;
10             }
11         }
12         return null;
13     }

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

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

 1 @Override
 2     public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
 3         Object handler = getHandlerInternal(request);
 4         if (handler == null) {
 5             handler = getDefaultHandler();
 6         }
 7         if (handler == null) {
 8             return null;
 9         }
10         // Bean name or resolved handler?
11         if (handler instanceof String) {
12             String handlerName = (String) handler;
13             handler = getApplicationContext().getBean(handlerName);
14         }
15         return getHandlerExecutionChain(handler, request);
16     }

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

spring mvc中DispatcherServlet如何得到ModelAndView的_第2张图片

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

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

 1 protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
 2         HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
 3                 (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
 4         chain.addInterceptors(getAdaptedInterceptors());
 5 
 6         String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
 7         for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) {
 8             if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
 9                 chain.addInterceptor(mappedInterceptor.getInterceptor());
10             }
11         }
12 
13         return chain;
14     }

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

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

 1     protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
 2         for (HandlerAdapter ha : this.handlerAdapters) {
 3             if (logger.isTraceEnabled()) {
 4                 logger.trace("Testing handler adapter [" + ha + "]");
 5             }
 6             if (ha.supports(handler)) {
 7                 return ha;
 8             }
 9         }
10         throw new ServletException("No adapter for handler [" + handler +
11                 "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
12     }

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

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

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

 1 private ModelAndView invokeHandleMethod(HttpServletRequest request,
 2             HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
 3 
 4         ServletWebRequest webRequest = new ServletWebRequest(request, response);
 5 
 6         WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
 7         ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
 8         ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);
 9 
10         ModelAndViewContainer mavContainer = new ModelAndViewContainer();
11         mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
12         modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
13         mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
14 
15         AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
16         asyncWebRequest.setTimeout(this.asyncRequestTimeout);
17 
18         final WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
19         asyncManager.setTaskExecutor(this.taskExecutor);
20         asyncManager.setAsyncWebRequest(asyncWebRequest);
21         asyncManager.registerCallableInterceptors(this.callableInterceptors);
22         asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
23 
24         if (asyncManager.hasConcurrentResult()) {
25             Object result = asyncManager.getConcurrentResult();
26             mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
27             asyncManager.clearConcurrentResult();
28 
29             if (logger.isDebugEnabled()) {
30                 logger.debug("Found concurrent result value [" + result + "]");
31             }
32             requestMappingMethod = requestMappingMethod.wrapConcurrentResult(result);
33         }
34 
35         requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
36 
37         if (asyncManager.isConcurrentHandlingStarted()) {
38             return null;
39         }
40 
41         return getModelAndView(mavContainer, modelFactory, webRequest);
42     }

终于知道为什么需要适配器了,原来这个适配器的方法中,就适配了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这个函数:

 1 protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
 2         String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
 3         if (logger.isDebugEnabled()) {
 4             logger.debug("Looking up handler method for path " + lookupPath);
 5         }
 6         HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
 7         if (logger.isDebugEnabled()) {
 8             if (handlerMethod != null) {
 9                 logger.debug("Returning handler method [" + handlerMethod + "]");
10             }
11             else {
12                 logger.debug("Did not find handler method for [" + lookupPath + "]");
13             }
14         }
15         return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
16     }

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

 1 protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
 2         List matches = new ArrayList();
 3         List directPathMatches = this.urlMap.get(lookupPath);
 4         if (directPathMatches != null) {
 5             addMatchingMappings(directPathMatches, matches, request);
 6         }
 7         if (matches.isEmpty()) {
 8             // No choice but to go through all mappings...
 9             addMatchingMappings(this.handlerMethods.keySet(), matches, request);
10         }
11 
12         if (!matches.isEmpty()) {
13             Comparator comparator = new MatchComparator(getMappingComparator(request));
14             Collections.sort(matches, comparator);
15             if (logger.isTraceEnabled()) {
16                 logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
17             }
18             Match bestMatch = matches.get(0);
19             if (matches.size() > 1) {
20                 Match secondBestMatch = matches.get(1);
21                 if (comparator.compare(bestMatch, secondBestMatch) == 0) {
22                     Method m1 = bestMatch.handlerMethod.getMethod();
23                     Method m2 = secondBestMatch.handlerMethod.getMethod();
24                     throw new IllegalStateException(
25                             "Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" +
26                             m1 + ", " + m2 + "}");
27                 }
28             }
29             handleMatch(bestMatch.mapping, lookupPath, request);
30             return bestMatch.handlerMethod;
31         }
32         else {
33             return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
34         }
35     }

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

1     private void addMatchingMappings(Collection mappings, List matches, HttpServletRequest request) {
2         for (T mapping : mappings) {
3             T match = getMatchingMapping(mapping, request);
4             if (match != null) {
5                 matches.add(new Match(match, this.handlerMethods.get(mapping)));
6             }
7         }
8     }

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

spring mvc中DispatcherServlet如何得到ModelAndView的_第3张图片

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

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

 1 protected void initHandlerMethods() {
 2     if (logger.isDebugEnabled()) {
 3         logger.debug("Looking for request mappings in application context: " + getApplicationContext());
 4     }
 5 
 6     String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
 7             BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
 8             getApplicationContext().getBeanNamesForType(Object.class));
 9 
10     for (String beanName : beanNames) {
11         if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) &&
12                 isHandler(getApplicationContext().getType(beanName))){
13             detectHandlerMethods(beanName);
14         }
15     }
16     handlerMethodsInitialized(getHandlerMethods());
17 }

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

 1 protected void detectHandlerMethods(final Object handler) {
 2         Class handlerType =
 3                 (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
 4 
 5         // Avoid repeated calls to getMappingForMethod which would rebuild RequestMappingInfo instances
 6         final Map mappings = new IdentityHashMap();
 7         final Class userType = ClassUtils.getUserClass(handlerType);
 8 
 9         Set methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
10             @Override
11             public boolean matches(Method method) {
12                 T mapping = getMappingForMethod(method, userType);
13                 if (mapping != null) {
14                     mappings.put(method, mapping);
15                     return true;
16                 }
17                 else {
18                     return false;
19                 }
20             }
21         });
22 
23         for (Method method : methods) {
24             registerHandlerMethod(handler, method, mappings.get(method));
25         }
26     }

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

 1 protected void registerHandlerMethod(Object handler, Method method, T mapping) {
 2         HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
 3         HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);
 4         if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {
 5             throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean() +
 6                     "' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '" +
 7                     oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");
 8         }
 9 
10         this.handlerMethods.put(mapping, newHandlerMethod);
11         if (logger.isInfoEnabled()) {
12             logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);
13         }
14 
15         Set patterns = getMappingPathPatterns(mapping);
16         for (String pattern : patterns) {
17             if (!getPathMatcher().isPattern(pattern)) {
18                 this.urlMap.add(pattern, mapping);
19             }
20         }
21     }

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

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

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

转载于:https://www.cnblogs.com/JMLiu/p/10029311.html

你可能感兴趣的:(测试,java)