SpringMVC源码分析ModelAndView的形成过程

首先,我们还是从 DispatcherServlet .doDispatch( HttpServletRequest request, HttpServletResponse response) throws Exception方法开始,看看ModelAndView是怎么开始的,又是怎么结束的:

 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;
             }
 
             // Actually invoke the handler.调用handler方法,返回ModelAndView类型的对象
             mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
 
             if (asyncManager.isConcurrentHandlingStarted()) {
                 return;
             }
        // 设置mv的Object view属性值,是一个String类型(依据request的URI计算,加上一个前缀和后缀得到)
             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
             if (mappedHandler != null) {
                 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
             }
         }
         else {
             // Clean up any resources used by a multipart request.
             if (multipartRequestParsed) {
                 cleanupMultipart(processedRequest);
             }
         }
     }
 }
从上面的源代码可以看出,mv是在调用handler方法的时候返回的(即便,我们@RequestMapping注解标识的handler方法很多时候返回的是一个String,或者是View类型。但是,反射是无所不能的,你懂的...)。我们知道,@RequestMapping标注的handler方法通常能够返回三种类型的结果:String,ModelAndView和View。从上面的ha.handle方法我们知道,动态代理最后都会将结果转化成ModelAndView类型。
一、得到mv

下面对 mv = ha.handle(processedRequest, response, mappedHandler.getHandler())方法中如何得到mv实例的演变过程进行分析:

从 SpringMVC源码分析 中分析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 的源代码:

 /**
  * Invoke the RequestMapping handler method preparing a ModelAndView if view resolution is required.
  */
 private ModelAndView invokeHandleMethod(HttpServletRequest request,
         HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
 
    //step 1、ModelAndView的前世:是一个ModelAndViewContainer实例
    ModelAndViewContainer mavContainer = new ModelAndViewContainer();
     
    /*
    * RequestContextUtils.getInputFlashMap(request)可以获取到request中的attribute,
    * 并且将所有的request中的attribute放置在mavContainer中,此时使用的是defaultModel
    */
     mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
     modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
    /* 
     * 在RequestMappingHandlerAdapter中ignoreDefaultModelOnRedirect默认为false
     */
     mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
     
     //省略许多代码...
     
     if (asyncManager.hasConcurrentResult()) {
             //...
             mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
             
             //...
         }
 
     //step 2、调用handler方法,
     requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
     
     //省略许多代码...
 
     //step 3、从mavContainer中获取到ModelAndView实例,返回
     return getModelAndView(mavContainer, modelFactory, webRequest);
 }
看看上面代码中ModelAndViewContainer是个什么东西?同样从SpringMVC源码分析中我们可以得出如下的结论:

①、 ModelMap实际上就是一个LinkedHashMap <String, Object> ,可以用于存放key-value的集合;
②、 ModelAndViewContainer含有两个ModelMap对象 : defaultModel和redirectModel,默认情况下使用defaultModel ;
有了以上两个结论,我们还有必要分析一下ModelAndViewContainer的一些细节(主要参看下面代码中的注释):

 public class ModelAndViewContainer {
     /*
      * 对这7个属性我们可以将其分为3个部分来看
      */
 
     // 1、两个模型和一个view,其中view可以存放任何java类型
     private Object view;
 
     // * 注意这个是final类型,也就是不能再重新为defaultModel赋值
     private final ModelMap defaultModel = new BindingAwareModelMap();
 
     private ModelMap redirectModel;
 
     // 2、记录当前的Model使用策略
     private boolean ignoreDefaultModelOnRedirect = false;
 
     private boolean redirectModelScenario = false;
 
     // 3、记录request和session的处理状态
     private final SessionStatus sessionStatus = new SimpleSessionStatus();
 
     private boolean requestHandled = false;
     
   //和view属性相关的处理方法
   /*
    * 对view的处理主要分为String类型和非String类型。如果views是一个
    * String类型的对象,那么它就是下面说的isViewReference
    */
     public void setViewName(String viewName) {
         this.view = viewName;
     }
     
     public String getViewName() {
         return (this.view instanceof String ? (String) this.view : null);
     }
     
     public void setView(Object view) {
         this.view = view;
     }
     
     public Object getView() {
         return this.view;
     }
     
     /*
      * 注意,如果veiw是一个String类型,那么isViewReference
      */
     public boolean isViewReference() {
         return (this.view instanceof String);
     }
     
     //和model相关的处理方法
     /*
      * 假设redirectModelScenario = R ,ignoreDefaultModelOnRedirect = I ,(redirectModel == null)= M
      * 那么(R, I, M)共有8中组合情况,useDefaultModel返回false(也就是使用redirectModel)只有三种情况:
      * (1,1,0)、(1,1,1)、(1,0,0)
      * a:如果同时设置了redirectModelScenario和ignoreDefaultModelOnRedirect为true,那么无论redirectModel
      *    是否为null,都会使用redirectModel;
      * b:如果设置了redirectModelScenario为true,而ignoreDefaultModelOnRedirect为false,同时redirectModel
      *    为null,那么也会使用redirectModel;
      */
     private boolean useDefaultModel() {
         return (!this.redirectModelScenario || (!this.ignoreDefaultModelOnRedirect  && this.redirectModel == null));
     }
     
     /*
      * 这个方法是重要的,通过设置redirectModelScenario和ignoreDefaultModelOnRedirect来影响该方法的返回值;
      * 如果没有初始化redirectModel,那么就会new一个ModelMap对象进行返回;
      */
     public ModelMap getModel() {
         if (useDefaultModel()) {
             return this.defaultModel;
         }
         else {
             return (this.redirectModel != null) ? this.redirectModel : new ModelMap();
         }
     }
     
     public ModelMap getDefaultModel() {
         return this.defaultModel;
     }
     
     public void setRedirectModel(ModelMap redirectModel) {
         this.redirectModel = redirectModel;
     }
     
     /**
      * Whether the controller has returned a redirect instruction, e.g. a
      * "redirect:" prefixed view name, a RedirectView instance, etc.
      */
     public void setRedirectModelScenario(boolean redirectModelScenario) {
         this.redirectModelScenario = redirectModelScenario;
     }
     
     public void setIgnoreDefaultModelOnRedirect(boolean ignoreDefaultModelOnRedirect) {
         this.ignoreDefaultModelOnRedirect = ignoreDefaultModelOnRedirect;
     }
     
     //省略一些方法...
 }
看看 requestMappingMethod.invokeAndHandle(webRequest, mavContainer); 中对mavContainer做了什么事情?看看 requestMappingMethod.invokeAndHandle(webRequest, mavContainer) 的源代码:
 /**
  *Invokes the method and handles the return value through one of the configured HandlerMethodReturnValueHandlers.
  */
 public void invokeAndHandle(ServletWebRequest webRequest,
         ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
     
     // 调用handler方法,的到返回结果为returnValue
     Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
     setResponseStatus(webRequest);
 
     if (returnValue == null) {//handler返回null
         if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
             mavContainer.setRequestHandled(true);
             return;
         }
     }else if (StringUtils.hasText(this.responseReason)) {
         mavContainer.setRequestHandled(true);
         return;
     }
 
     // 设置标志位,表示当前request请求还没有处理完成
     mavContainer.setRequestHandled(false);
     try {
         // 处理返回结果returnValue
         this.returnValueHandlers.handleReturnValue(
                 returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
     }catch (Exception ex) {
         if (logger.isTraceEnabled()) {
             logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
         }
         throw ex;
     }
 }
看看上面代码中是如何处理返回结果returnValue的, this .returnValueHandlers.handleReturnValue 实际上调用的是 HandlerMethodReturnValueHandlerComposite .handleReturnValue方法,代码分析如下:
 public void handleReturnValue(Object returnValue, MethodParameter returnType,
         ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
 
     //寻找能够处理returnType类型的HandlerMethodReturnValueHandler实例
     HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType);
     
     //如果没有找到对应的HandlerMethodReturnValueHandler,则会返回一个异常
     Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
     
     //用上面寻找到的handler来处理返回结果
     handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
 }
从上面的代码看出,处理返回结果分为两个步骤:①、寻找能够处理 returnType 类型的HandlerMethodReturnValueHandler,如果没有找到,则会抛出异常;②、利用已经找到的handler来处理返回结果;

同时,我们要注意HandlerMethodReturnValueHandler是一个接口类型,该接口只有两个方法:boolean supportsReturnType(MethodParameter returnType)和void handleReturnValue(…)。看看它有多少个实现类:

既然是这样,我们就像看看到底是如何确定处理返回结果的handler的呢??同时,又能够处理多少种不同的handler返回类型呢??

看看 getReturnValueHandler(returnType) 方法的源代码:

 /**
  * Find a registered HandlerMethodReturnValueHandler that supports the given return type
  */
 private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
     /*
      * returnValueHandlers是List<HandlerMethodReturnValueHandler>类型,
      * 在调试的时候可以看到它共包含了13个对象。还可以增减吗??如何注
      * 册一个returnValueHandler呢??有待探究。
      */
     for (HandlerMethodReturnValueHandler returnValueHandler : returnValueHandlers) {
         if (logger.isTraceEnabled()) {
             logger.trace("Testing if return value handler [" + returnValueHandler + "] supports [" +
                     returnType.getGenericParameterType() + "]");
         }
         if (returnValueHandler.supportsReturnType(returnType)) {
             return returnValueHandler;
         }
     }
     return null;
 }
我们可以看到,在 getReturnValueHandler 方法中会遍历 returnValueHandlers (实际上是一个 List<HandlerMethodReturnValueHandler> 类型),如果遇到能够支持返回值类型的handler,则将其返回。在调试中看看 returnValueHandlers 链表中都有哪些对象:

可以看见, returnValueHandlers 链表中一共有14个对象,对应于上面说的HandlerMethodReturnValueHandler接口实现类中除了 HandlerMethodReturnValueHandlerComposite 类之外的其它实现类对象。这个有点儿意思哈(_)。

如果在request请求处理的handler方法中返回String类型,则其 getReturnValueHandler(returnType) 方法就会返回一个ViewNameMethodReturnValueHandler类型的实例。看看ViewNameMethodReturnValueHandler类的定义:

 public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
 
     private String[] redirectPatterns;
 
     public void setRedirectPatterns(String... redirectPatterns) {
         this.redirectPatterns = redirectPatterns;
     }
 
     public String[] getRedirectPatterns() {
         return this.redirectPatterns;
     }
 
 
     @Override
     public boolean supportsReturnType(MethodParameter returnType) {
         Class<?> paramType = returnType.getParameterType();
         return (void.class.equals(paramType) || String.class.equals(paramType));
     }
 
     @Override
     public void handleReturnValue(Object returnValue, MethodParameter returnType,
             ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
 
         if (returnValue == null) {
             return;
         }
         else if (returnValue instanceof String) {
             String viewName = (String) returnValue;
             
             // 对mavContainer的设置就是将view属性赋值为returnValue
             mavContainer.setViewName(viewName);
             
             /* 如果returnValue指示为redirect(比如说"redirect:"前缀,redirect pattern的设置等)
              * 则将设置redirectModelScenario为true。
              */
             if (isRedirectViewName(viewName)) {
                 mavContainer.setRedirectModelScenario(true);
             }
         }
         else {
             // should not happen
             throw new UnsupportedOperationException("Unexpected return type: " +
                     returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
         }
     }
 
     /**
      * Whether the given view name is a redirect view reference.
      * The default implementation checks the configured redirect patterns and
      * also if the view name starts with the "redirect:" prefix.
      * @param viewName the view name to check, never {@code null}
      * @return "true" if the given view name is recognized as a redirect view
      * reference; "false" otherwise.
      */
     protected boolean isRedirectViewName(String viewName) {
         if (PatternMatchUtils.simpleMatch(this.redirectPatterns, viewName)) {
             return true;
         }
         return viewName.startsWith("redirect:");
     }
 
 }
到现在为止,request请求的handler方法也调用了。同时根据handler方法的返回结果调用不同的Xxx ReturnValueHandler的 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中还需要做哪些设置:

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 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值