spring mvc中DispatcherServlet如何得到ModelAndView的
首先看下面这种张图,这张图说明了spring mvc整体的流程。
本文讲的就是如何从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<String, Object> attributesSnapshot = null; 11 if (WebUtils.isIncludeRequest(request)) { 12 attributesSnapshot = new HashMap<String, Object>(); 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 * <p>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 * <p>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对象存的是什么呢?我们看看:
原来是它封装了我们映射到的控制器,包括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<Match> matches = new ArrayList<Match>(); 3 List<T> 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<Match> 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<T> mappings, List<Match> 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 里有啥:
果然我们写的控制器方法对应的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<Method, T> mappings = new IdentityHashMap<Method, T>(); 7 final Class<?> userType = ClassUtils.getUserClass(handlerType); 8 9 Set<Method> 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<String> 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的过程就讲完了。