8、SpringMVC源码分析(3):分析ModelAndView的形成过程
2015-11-19 20:56 宏愿。 阅读(1374) 评论(0) 编辑 收藏 举报
首先,我们还是从DispatcherServlet.doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception方法开始,看看这个牛逼的ModelAndView是怎么开始的,又是怎么结束的:
1 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { 2 HttpServletRequest processedRequest = request; 3 HandlerExecutionChain mappedHandler = null; 4 boolean multipartRequestParsed = false; 5 6 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 7 8 try { 9 ModelAndView mv = null; 10 Exception dispatchException = null; 11 12 try { 13 processedRequest = checkMultipart(request); 14 multipartRequestParsed = (processedRequest != request); 15 16 // Determine handler for the current request. 17 mappedHandler = getHandler(processedRequest); 18 if (mappedHandler == null || mappedHandler.getHandler() == null) { 19 noHandlerFound(processedRequest, response); 20 return; 21 } 22 23 // Determine handler adapter for the current request. 24 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); 25 26 // Process last-modified header, if supported by the handler. 27 String method = request.getMethod(); 28 boolean isGet = "GET".equals(method); 29 if (isGet || "HEAD".equals(method)) { 30 long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); 31 if (logger.isDebugEnabled()) { 32 logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); 33 } 34 if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { 35 return; 36 } 37 } 38 39 if (!mappedHandler.applyPreHandle(processedRequest, response)) { 40 return; 41 } 42 43 // Actually invoke the handler.调用handler方法,返回ModelAndView类型的对象 44 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 45 46 if (asyncManager.isConcurrentHandlingStarted()) { 47 return; 48 } 49 // 设置mv的Object view属性值,是一个String类型(依据request的URI计算,加上一个前缀和后缀得到) 50 applyDefaultViewName(request, mv); 51 mappedHandler.applyPostHandle(processedRequest, response, mv); 52 } 53 catch (Exception ex) { 54 dispatchException = ex; 55 } 56 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 57 } 58 catch (Exception ex) { 59 triggerAfterCompletion(processedRequest, response, mappedHandler, ex); 60 } 61 catch (Error err) { 62 triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err); 63 } 64 finally { 65 if (asyncManager.isConcurrentHandlingStarted()) { 66 // Instead of postHandle and afterCompletion 67 if (mappedHandler != null) { 68 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); 69 } 70 } 71 else { 72 // Clean up any resources used by a multipart request. 73 if (multipartRequestParsed) { 74 cleanupMultipart(processedRequest); 75 } 76 } 77 } 78 }
从上面的源代码可以看出,mv是在调用handler方法的时候返回的(即便,我们@RequestMapping注解标识的handler方法很多时候返回的是一个String,或者是View类型。但是,反射是无所不能的,你懂的...)。我们知道,@RequestMapping标注的handler方法通常能够返回三种类型的结果:String,ModelAndView和View。从上面的ha.handle方法我们知道,动态代理最后都会将结果转化成ModelAndView类型。
一、得到mv
下面对mv = ha.handle(processedRequest, response, mappedHandler.getHandler())方法中如何得到mv实例的演变过程进行分析:
从7、SpringMVC源码分析(2)中分析ha.handle方法的流程时我们知道:
①、HandlerAdapter是一个接口类型;
②、AbstractHandlerMethodAdapter是一个抽象类,该抽象类实现了HandlerAdapter接口;
③、AbstractHandlerMethodAdapter的handle是一个public final类型的具体方法,此方法直接调用了AbstractHandlerMethodAdapter中protected abstract类型的抽象方法handleInternal(该方法由具体的子类来实现);
④、HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler())语句会从mappedHandler队列中找到第一个能够处理该请求的HandlerAdapter的具体实现的一个实例,将其向上转型为HandlerAdapter并且赋值给ha;
⑤、@RequestMapping标注的handler方法都由RequestMappingHandlerAdapter来处理,这个Adapter继承自抽象类AbstractHandlerMethodAdapter。所以,AbstractHandlerMethodAdapter.handleInternal方法由RequestMappingHandlerAdapter.handleInternal来实现;
⑥、总结上面①~⑤的分析,我们可以得出这样一个简单的结论:doDispatch中的ha.handle方法实际上是调用了RequestMappingHandlerAdapter.handleInternal方法;
有了以上⑥的结论以后,我们继续追踪ModelAndView的源头,从RequestMappingHandlerAdapter.handleInternal追踪到RequestMappingHandlerAdapter.invokeHandleMethod,我们来看一看invokeHandleMethod的源代码:
【代码片段2】:
1 /** 2 * Invoke the RequestMapping handler method preparing a ModelAndView if view resolution is required. 3 */ 4 private ModelAndView invokeHandleMethod(HttpServletRequest request, 5 HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { 6 7 //step 1、ModelAndView的前世:是一个ModelAndViewContainer实例 8 ModelAndViewContainer mavContainer = new ModelAndViewContainer(); 9 10 /* 11 * RequestContextUtils.getInputFlashMap(request)可以获取到request中的attribute, 12 * 并且将所有的request中的attribute放置在mavContainer中,此时使用的是defaultModel 13 */ 14 mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); 15 modelFactory.initModel(webRequest, mavContainer, requestMappingMethod); 16 /* 17 * 在RequestMappingHandlerAdapter中ignoreDefaultModelOnRedirect默认为false 18 */ 19 mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); 20 21 //省略许多代码... 22 23 if (asyncManager.hasConcurrentResult()) { 24 //... 25 mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0]; 26 27 //... 28 } 29 30 //step 2、调用handler方法, 31 requestMappingMethod.invokeAndHandle(webRequest, mavContainer); 32 33 //省略许多代码... 34 35 //step 3、从mavContainer中获取到ModelAndView实例,返回 36 return getModelAndView(mavContainer, modelFactory, webRequest); 37 }
我们看看【代码片段2】中step1:
ModelAndViewContainer是个什么东西?同样从7、SpringMVC源码分析(2)的step1中我们可以得出如下的结论:
①、ModelMap实际上就是一个LinkedHashMap<String, Object>,可以用于存放key-value的集合;
②、ModelAndViewContainer含有两个ModelMap对象:defaultModel和redirectModel,默认情况下使用defaultModel;
有了以上两个结论,我们还有必要分析一下ModelAndViewContainer的一些细节(主要参看下面代码中的注释):
【代码片段3】:
1 public class ModelAndViewContainer { 2 /* 3 * 对这7个属性我们可以将其分为3个部分来看 4 */ 5 6 // 1、两个模型和一个view,其中view可以存放任何java类型 7 private Object view; 8 9 // * 注意这个是final类型,也就是不能再重新为defaultModel赋值 10 private final ModelMap defaultModel = new BindingAwareModelMap(); 11 12 private ModelMap redirectModel; 13 14 // 2、记录当前的Model使用策略 15 private boolean ignoreDefaultModelOnRedirect = false; 16 17 private boolean redirectModelScenario = false; 18 19 // 3、记录request和session的处理状态 20 private final SessionStatus sessionStatus = new SimpleSessionStatus(); 21 22 private boolean requestHandled = false; 23 24 //和view属性相关的处理方法 25 /* 26 * 对view的处理主要分为String类型和非String类型。如果views是一个 27 * String类型的对象,那么它就是下面说的isViewReference 28 */ 29 public void setViewName(String viewName) { 30 this.view = viewName; 31 } 32 33 public String getViewName() { 34 return (this.view instanceof String ? (String) this.view : null); 35 } 36 37 public void setView(Object view) { 38 this.view = view; 39 } 40 41 public Object getView() { 42 return this.view; 43 } 44 45 /* 46 * 注意,如果veiw是一个String类型,那么isViewReference 47 */ 48 public boolean isViewReference() { 49 return (this.view instanceof String); 50 } 51 52 //和model相关的处理方法 53 /* 54 * 假设redirectModelScenario = R ,ignoreDefaultModelOnRedirect = I ,(redirectModel == null)= M 55 * 那么(R, I, M)共有8中组合情况,useDefaultModel返回false(也就是使用redirectModel)只有三种情况: 56 * (1,1,0)、(1,1,1)、(1,0,0) 57 * a:如果同时设置了redirectModelScenario和ignoreDefaultModelOnRedirect为true,那么无论redirectModel 58 * 是否为null,都会使用redirectModel; 59 * b:如果设置了redirectModelScenario为true,而ignoreDefaultModelOnRedirect为false,同时redirectModel 60 * 为null,那么也会使用redirectModel; 61 */ 62 private boolean useDefaultModel() { 63 return (!this.redirectModelScenario || (!this.ignoreDefaultModelOnRedirect && this.redirectModel == null)); 64 } 65 66 /* 67 * 这个方法是重要的,通过设置redirectModelScenario和ignoreDefaultModelOnRedirect来影响该方法的返回值; 68 * 如果没有初始化redirectModel,那么就会new一个ModelMap对象进行返回; 69 */ 70 public ModelMap getModel() { 71 if (useDefaultModel()) { 72 return this.defaultModel; 73 } 74 else { 75 return (this.redirectModel != null) ? this.redirectModel : new ModelMap(); 76 } 77 } 78 79 public ModelMap getDefaultModel() { 80 return this.defaultModel; 81 } 82 83 public void setRedirectModel(ModelMap redirectModel) { 84 this.redirectModel = redirectModel; 85 } 86 87 /** 88 * Whether the controller has returned a redirect instruction, e.g. a 89 * "redirect:" prefixed view name, a RedirectView instance, etc. 90 */ 91 public void setRedirectModelScenario(boolean redirectModelScenario) { 92 this.redirectModelScenario = redirectModelScenario; 93 } 94 95 public void setIgnoreDefaultModelOnRedirect(boolean ignoreDefaultModelOnRedirect) { 96 this.ignoreDefaultModelOnRedirect = ignoreDefaultModelOnRedirect; 97 } 98 99 //省略一些方法... 100 }
接下来分析【代码片段2】中step2:
看看requestMappingMethod.invokeAndHandle(webRequest, mavContainer);中对mavContainer做了什么事情?看看requestMappingMethod.invokeAndHandle(webRequest, mavContainer)的源代码:
【代码片段4】:
1 /** 2 *Invokes the method and handles the return value through one of the configured HandlerMethodReturnValueHandlers. 3 */ 4 public void invokeAndHandle(ServletWebRequest webRequest, 5 ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { 6 7 // 调用handler方法,的到返回结果为returnValue 8 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); 9 setResponseStatus(webRequest); 10 11 if (returnValue == null) {//handler返回null 12 if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) { 13 mavContainer.setRequestHandled(true); 14 return; 15 } 16 }else if (StringUtils.hasText(this.responseReason)) { 17 mavContainer.setRequestHandled(true); 18 return; 19 } 20 21 // 设置标志位,表示当前request请求还没有处理完成 22 mavContainer.setRequestHandled(false); 23 try { 24 // 处理返回结果returnValue 25 this.returnValueHandlers.handleReturnValue( 26 returnValue, getReturnValueType(returnValue), mavContainer, webRequest); 27 }catch (Exception ex) { 28 if (logger.isTraceEnabled()) { 29 logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex); 30 } 31 throw ex; 32 } 33 }
看看上面代码中是如何处理返回结果returnValue的,this.returnValueHandlers.handleReturnValue实际上调用的是HandlerMethodReturnValueHandlerComposite.handleReturnValue方法,代码分析如下:
【代码片段5】:
1 public void handleReturnValue(Object returnValue, MethodParameter returnType, 2 ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { 3 4 //寻找能够处理returnType类型的HandlerMethodReturnValueHandler实例 5 HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType); 6 7 //如果没有找到对应的HandlerMethodReturnValueHandler,则会返回一个异常 8 Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]"); 9 10 //用上面寻找到的handler来处理返回结果 11 handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); 12 }
从上面的代码看出,处理返回结果分为两个步骤:①、寻找能够处理returnType类型的HandlerMethodReturnValueHandler,如果没有找到,则会抛出异常;②、利用已经找到的handler来处理返回结果;
同时,我们要注意HandlerMethodReturnValueHandler是一个接口类型,该接口只有两个方法:boolean supportsReturnType(MethodParameter returnType)和void handleReturnValue(...)。看看它有多少个实现类:
既然是这样,我们就像看看到底是如何确定处理返回结果的handler的呢??同时,又能够处理多少种不同的handler返回类型呢??
看看getReturnValueHandler(returnType)方法的源代码:
【代码片段6】:
1 /** 2 * Find a registered HandlerMethodReturnValueHandler that supports the given return type 3 */ 4 private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) { 5 /* 6 * returnValueHandlers是List<HandlerMethodReturnValueHandler>类型, 7 * 在调试的时候可以看到它共包含了13个对象。还可以增减吗??如何注 8 * 册一个returnValueHandler呢??有待探究。 9 */ 10 for (HandlerMethodReturnValueHandler returnValueHandler : returnValueHandlers) { 11 if (logger.isTraceEnabled()) { 12 logger.trace("Testing if return value handler [" + returnValueHandler + "] supports [" + 13 returnType.getGenericParameterType() + "]"); 14 } 15 if (returnValueHandler.supportsReturnType(returnType)) { 16 return returnValueHandler; 17 } 18 } 19 return null; 20 }
我们可以看到,在getReturnValueHandler方法中会遍历returnValueHandlers(实际上是一个List<HandlerMethodReturnValueHandler>类型),如果遇到能够支持返回值类型的handler,则将其返回。在调试中看看returnValueHandlers链表中都有哪些对象:
可以看见,returnValueHandlers链表中一共有14个对象,对应于上面说的HandlerMethodReturnValueHandler接口实现类中除了HandlerMethodReturnValueHandlerComposite类之外的其它实现类对象。这个有点儿意思哈(^_^)。
如果在request请求处理的handler方法中返回String类型,则其getReturnValueHandler(returnType)方法就会返回一个ViewNameMethodReturnValueHandler类型的实例。看看ViewNameMethodReturnValueHandler类的定义:
【代码片段7】:
1 public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValueHandler { 2 3 private String[] redirectPatterns; 4 5 public void setRedirectPatterns(String... redirectPatterns) { 6 this.redirectPatterns = redirectPatterns; 7 } 8 9 public String[] getRedirectPatterns() { 10 return this.redirectPatterns; 11 } 12 13 14 @Override 15 public boolean supportsReturnType(MethodParameter returnType) { 16 Class<?> paramType = returnType.getParameterType(); 17 return (void.class.equals(paramType) || String.class.equals(paramType)); 18 } 19 20 @Override 21 public void handleReturnValue(Object returnValue, MethodParameter returnType, 22 ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { 23 24 if (returnValue == null) { 25 return; 26 } 27 else if (returnValue instanceof String) { 28 String viewName = (String) returnValue; 29 30 // 对mavContainer的设置就是将view属性赋值为returnValue 31 mavContainer.setViewName(viewName); 32 33 /* 如果returnValue指示为redirect(比如说"redirect:"前缀,redirect pattern的设置等) 34 * 则将设置redirectModelScenario为true。 35 */ 36 if (isRedirectViewName(viewName)) { 37 mavContainer.setRedirectModelScenario(true); 38 } 39 } 40 else { 41 // should not happen 42 throw new UnsupportedOperationException("Unexpected return type: " + 43 returnType.getParameterType().getName() + " in method: " + returnType.getMethod()); 44 } 45 } 46 47 /** 48 * Whether the given view name is a redirect view reference. 49 * The default implementation checks the configured redirect patterns and 50 * also if the view name starts with the "redirect:" prefix. 51 * @param viewName the view name to check, never {@code null} 52 * @return "true" if the given view name is recognized as a redirect view 53 * reference; "false" otherwise. 54 */ 55 protected boolean isRedirectViewName(String viewName) { 56 if (PatternMatchUtils.simpleMatch(this.redirectPatterns, viewName)) { 57 return true; 58 } 59 return viewName.startsWith("redirect:"); 60 } 61 62 }
到现在为止,request请求的handler方法也调用了。同时根据handler方法的返回结果调用不同的XxxReturnValueHandler的handleReturnValue方法对mavContainer的view及其状态进行了设置。
我们分析出:如果handler方法的返回类型是String的话,①设置mavContainer的Object view为该返回字符串;②如果返回字符串中以“redirect:”开头,则设置redirectModelScenario为true(也可以通过redirect pattern来设置,这种方式目前还不熟悉??)。
接下来就是通过设置好的mavContainer来得到一个ModelAndView。
最后就是分析【代码片段2】中step3:
具体就是执行 return getModelAndView(mavContainer, modelFactory, webRequest) 返回一个ModelAndView,成功的将handler的各种类型的返回结果转换成ModelAndView。
看看getModelAndView源代码,分析其流程,我们可以再反过来推step2中还需要做哪些设置:
【代码片段8】:
1 private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, 2 ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception { 3 4 modelFactory.updateModel(webRequest, mavContainer); 5 if (mavContainer.isRequestHandled()) { 6 return null; 7 } 8 9 //得到defaultModel或者是redirectModel 10 ModelMap model = mavContainer.getModel(); 11 12 /* 13 * 注意mavContainer.getViewName()方法,如果mavContainer的Object view是一个String类型, 14 * 则返回该字符串;如果不是,则得到null 15 */ 16 ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model); 17 18 if (!mavContainer.isViewReference()) { 19 mav.setView((View) mavContainer.getView()); 20 } 21 if (model instanceof RedirectAttributes) { 22 Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); 23 HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); 24 RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); 25 } 26 return mav; 27 }