SpringMVC源码解析六(ModelAndView解析)

在上一篇博客中解析到在InvocableHandlerMethod#doInvoke()中通过反射执行处理方法, 返回值为ModelAndView实例

InvocableHandlerMethod#doInvoke()方法调用链:

SpringMVC源码解析六(ModelAndView解析)_第1张图片

RequestMappingHandlerAdapter#invokeHandlerMethod()方法中主要完成了两件事:

  1. 执行处理器方法(HandlerMethod)
  2. 封装并返回ModelAndView实例

(1) 执行处理方法以及封装ModelAndView

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    //将Request和Response进行封装
    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {
        WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
        ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
        /**
         * 创建InvocableHandlerMethod实例,以及各个组件的配置;
         * 后面通过调用invokeAndHandle()方法执行处理器
         */
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);


        if (this.argumentResolvers != null) {
            //设置参数解析器
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }
        if (this.returnValueHandlers != null) {
            //设置返回值解析器
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }
        invocableMethod.setDataBinderFactory(binderFactory);
        invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
        /**
         * 创建视图容器, 用于封装视图, 数据模型, 处理状态等信息
         */
        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
        mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
        modelFactory.initModel(webRequest, mavContainer, invocableMethod);
        mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

        AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
        asyncWebRequest.setTimeout(this.asyncRequestTimeout);

        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 + "]");
            }
            invocableMethod = invocableMethod.wrapConcurrentResult(result);
        }
        /**
         * 1. 执行处理器
         */
        invocableMethod.invokeAndHandle(webRequest, mavContainer);
        if (asyncManager.isConcurrentHandlingStarted()) {
            return null;
        }
        /**
         * 2. 返回ModelAndView实例, 后面进行视图解析
         */
        return getModelAndView(mavContainer, modelFactory, webRequest);
    }
    finally {
        webRequest.requestCompleted();
    }
}

分析: 在invokeAndHandle()方法在执行处理器方法,并对返回值进行封装

ServletInvocableHandlerMethod#invokeAndHandle()方法实现:

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
    /**
     * 1. 处理请求
     * returnValue为返回的ModelAndView实例或者ViewName
     */
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    setResponseStatus(webRequest);

    if (returnValue == null) {
        if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
            mavContainer.setRequestHandled(true);
            return;
        }
    }
    else if (StringUtils.hasText(getResponseStatusReason())) {
        mavContainer.setRequestHandled(true);
        return;
    }

    mavContainer.setRequestHandled(false);
    Assert.state(this.returnValueHandlers != null, "No return value handlers");
    try {
        /**
         * 2. 封装返回的数据信息
         */
        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;
    }
}

分析: 关于处理方法的执行逻辑不再陈述, 相关逻辑可以看这篇博客:SpringMVC源码解析五(HandlerMethod执行过程解析); 这里着重分析ModelAndView的解析

HandlerMethodReturnValueHandlerComposite#handleReturnValue()方法实现:

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
    //根据返回值以及返回类型选择合适的返回值处理器, 对返回值进行解析
    HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
    if (handler == null) {
        throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
    }
    /**
     *  解析结果
     * {@link ModelAndViewMethodReturnValueHandler#handleReturnValue()}
     */
    handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

ModelAndViewMethodReturnValueHandler#handleReturnValue()方法实现:

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
      ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

   if (returnValue == null) {
      mavContainer.setRequestHandled(true);
      return;
   }

   /**
    * 1.获取视图
    */
   ModelAndView mav = (ModelAndView) returnValue;
   if (mav.isReference()) {
      String viewName = mav.getViewName();
      //设置视图名称
      mavContainer.setViewName(viewName);
      if (viewName != null && isRedirectViewName(viewName)) {
         mavContainer.setRedirectModelScenario(true);
      }
   }
   else {
      View view = mav.getView();
      //设置视图实例
      mavContainer.setView(view);
      if (view instanceof SmartView && ((SmartView) view).isRedirectView()) {
         mavContainer.setRedirectModelScenario(true);
      }
   }
   /**
    * 2.设置返回状态
    */
   mavContainer.setStatus(mav.getStatus());
   /**
    * 3.设置数据Model
    */
   mavContainer.addAllAttributes(mav.getModel());
}

分析: 将返回值强转为ModelAndView实例, 通过ModelAndView实例将返回状态, 返回视图以及数据模型信息封装到ModelAndViewContainer容器中

(2) 从ModelAndViewContainer容器中获取ModeAndView实例

RequestMappingHandlerAdapter#getModelAndView()方法实现

@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
      ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

   modelFactory.updateModel(webRequest, mavContainer);
   if (mavContainer.isRequestHandled()) {
      return null;
   }
   ModelMap model = mavContainer.getModel();
   /**
    * 根据从容器中获取的视图名称以及数据模型等信息创建ModelAndView实例
    */
   ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
   if (!mavContainer.isViewReference()) {
      mav.setView((View) mavContainer.getView());
   }
   if (model instanceof RedirectAttributes) {
      Map flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
      HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
      if (request != null) {
         RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
      }
   }
   return mav;
}

到这里可以知道ModelAndView是如何获取的, 接下来开始解析ModelAndView

DispatcherServlet#processDispatchResult()方法的实现:

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
      @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
      @Nullable Exception exception) throws Exception {

   boolean errorView = false;
   /**
    * 如果在解析过程中出现异常, 这里会对异常进行处理
    */
   if (exception != null) {
      //如果是默认异常,则获取异常视图
      if (exception instanceof ModelAndViewDefiningException) {
         logger.debug("ModelAndViewDefiningException encountered", exception);
         mv = ((ModelAndViewDefiningException) exception).getModelAndView();
      }
      //如果是自定义异常, 则获取异常处理器, 进行解析
      else {
         Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
         /**
          * 异常视图解析
          */
         mv = processHandlerException(request, response, handler, exception);
         errorView = (mv != null);
      }
   }

   // 处理程序是否返回了要渲染的视图?
   if (mv != null && !mv.wasCleared()) {
      /**
       * 渲染视图
       */
      render(mv, request, response);
      if (errorView) {
         WebUtils.clearErrorRequestAttributes(request);
      }
   }
   else {
      if (logger.isDebugEnabled()) {
         logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
               "': assuming HandlerAdapter completed request handling");
      }
   }

   if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
      // Concurrent handling started during a forward
      return;
   }

   if (mappedHandler != null) {
      mappedHandler.triggerAfterCompletion(request, response, null);
   }
}

分析: 

  1. 如果出现异常,则解析异常视图
  2. 解析ModelAndView

(1) DispatcherServlet#processHandlerException()方法实现:

@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
      @Nullable Object handler, Exception ex) throws Exception {

   //检查注册的HandlerExceptionResolvers ...
   ModelAndView exMv = null;
   if (this.handlerExceptionResolvers != null) {
      /**
       * 遍历所有的异常解析器, 尝试对异常进行解析, 如果解析成功,跳出循焕
       */
      for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
         exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
         if (exMv != null) {
            break;
         }
      }
   }
   if (exMv != null) {
      if (exMv.isEmpty()) {
         request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
         return null;
      }
      //对于简单的错误模型,我们可能仍需要视图名称转换
      if (!exMv.hasView()) {
         String defaultViewName = getDefaultViewName(request);
         if (defaultViewName != null) {
            exMv.setViewName(defaultViewName);
         }
      }
      if (logger.isDebugEnabled()) {
         logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
      }
      //设置错误请求的相关属性
      WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
      return exMv;
   }

   throw ex;
}

(2) DispatcherServlet#render()方法的实现:

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
   // Determine locale for request and apply it to the response.
   Locale locale =
         (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
   response.setLocale(locale);

   View view;
   String viewName = mv.getViewName();
   if (viewName != null) {
      /**
       * 根据ViewName解析view实例
       */
      view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
      if (view == null) {
         throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
               "' in servlet with name '" + getServletName() + "'");
      }
   }
   else {
      //无需查找:ModelAndView对象包含实际的View对象。
      view = mv.getView();
      if (view == null) {
         throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
               "View object in servlet with name '" + getServletName() + "'");
      }
   }

   // Delegate to the View object for rendering.
   if (logger.isDebugEnabled()) {
      logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
   }
   try {
      if (mv.getStatus() != null) {
         response.setStatus(mv.getStatus().value());
      }
      /**
       * 视图渲染
       * {@link AbstractView#render(java.util.Map, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
       */
      view.render(mv.getModelInternal(), request, response);
   }
   catch (Exception ex) {
      if (logger.isDebugEnabled()) {
         logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
               getServletName() + "'", ex);
      }
      throw ex;
   }
}

ModelAndView视图解析可以分为两个重要步骤: (1)根据视图名称创建View实例, (2)根据View实例渲染视图;  对于这两个操作SpringMVC抽象了两个接口ViewResolver和View, 这两个接口的定义如下:

public interface ViewResolver {
    // 通过逻辑视图名和用户地区信息生成View对象
    @Nullable
    View resolveViewName(String viewName, Locale locale) throws Exception;
}

public interface View {
    /**
     * 获取返回值的contentType
     */
    @Nullable
    default String getContentType() {
        return null;
    }
    /**
     * 通过用户提供的模型数据与视图信息渲染视图
     */
    void render(@Nullable Map model, HttpServletRequest request,HttpServletResponse response)throws Exception;
}

ViewResolver和View继承关系图如下:

SpringMVC源码解析六(ModelAndView解析)_第2张图片

SpringMVC源码解析六(ModelAndView解析)_第3张图片

视图解析执行链如下:

SpringMVC源码解析六(ModelAndView解析)_第4张图片

(1) 根据视图名称创建View实例

DispatcherServlet#resolveViewName()方法实现:

@Nullable
protected View resolveViewName(String viewName, @Nullable Map model,
      Locale locale, HttpServletRequest request) throws Exception {
   if (this.viewResolvers != null) {
      for (ViewResolver viewResolver : this.viewResolvers) {
         /**
          * ViewResolver ==> {@link org.springframework.web.servlet.view.InternalResourceViewResolver}
          * 这里的视图解析器就是我们在配置文件中配置的那个
          *
          * {@link AbstractCachingViewResolver#resolveViewName(String,Locale)}
          */
         View view = viewResolver.resolveViewName(viewName, locale);
         if (view != null) {
            return view;
         }
      }
   }
   return null;
}

分析: 视图实例View的创建委托给我们定义的视图解析器(InternalResourceViewResolver)来解析

AbstractCachingViewResolver#resolveViewName()方法实现:

@Override
@Nullable
public View resolveViewName(String viewName, Locale locale) throws Exception {
   //判断缓存是否可用
   if (!isCache()) {
      /**
       * 如果缓存不可用, 则直接创建视图
       */
      return createView(viewName, locale);
   }
   else {
      /**
       * 如果缓存可用, 则先尝试从缓存中获取
       */
      //生成缓存Key
      Object cacheKey = getCacheKey(viewName, locale);
      //尝试从缓存中获取视图
      View view = this.viewAccessCache.get(cacheKey);
      if (view == null) {
         /**
          * 如果从缓存中获取视图失败, 则尝试从viewCreationCache缓存中获取
          */
         synchronized (this.viewCreationCache) {
            view = this.viewCreationCache.get(cacheKey);
            if (view == null) {
               /**
                * 让子类创建View对象, 留给子类扩展[扩展开放,修改关闭原则]
                *{@link UrlBasedViewResolver#createView(java.lang.String, java.util.Locale)}
                */
               view = createView(viewName, locale);
               if (view == null && this.cacheUnresolved) {
                  // 这里cacheUnresolved指的是是否缓存默认的空视图,UNRESOLVED_VIEW是
                  // 一个没有任何内容的View
                  view = UNRESOLVED_VIEW;
               }
               if (view != null) {
                  //将创建的view视图加入缓存
                  this.viewAccessCache.put(cacheKey, view);
                  this.viewCreationCache.put(cacheKey, view);
                  if (logger.isTraceEnabled()) {
                     logger.trace("Cached view [" + cacheKey + "]");
                  }
               }
            }
         }
      }
      return (view != UNRESOLVED_VIEW ? view : null);
   }
}

分析: 首先判断缓存是否可用, 如果缓存不可用则直接创建视图; 如果缓存可用, 则尝试从缓存中获取, 如果从缓存中获取失败, 则加锁,创建视图, 在获取锁之后再次尝试从缓存中获取; 如果创建视图失败, 则将创建一个空视图返回;

UrlBasedViewResolver#createView()方法实现:

@Override
protected View createView(String viewName, Locale locale) throws Exception {
   // 如果此解析器不应该处理给定的视图,则返回null以传递到链中的下一个解析器。
   if (!canHandle(viewName, locale)) {
      return null;
   }

   /**
    * 检查特殊的"redirect:"前缀 REDIRECT_URL_PREFIX = "redirect:"
    * 如果是以"redirect:" 开头, 说明该视图是重定向
    */
   if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
      String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
      RedirectView view = new RedirectView(redirectUrl,
            isRedirectContextRelative(), isRedirectHttp10Compatible());
      String[] hosts = getRedirectHosts();
      if (hosts != null) {
         view.setHosts(hosts);
      }
      return applyLifecycleMethods(REDIRECT_URL_PREFIX, view);
   }

   /**
    * 检查特殊的"forward:"前缀 FORWARD_URL_PREFIX = "forward:"
    * 如果是以"forward:" 开头, 说明该视图是请求转发
    */
   if (viewName.startsWith(FORWARD_URL_PREFIX)) {
      String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
      return new InternalResourceView(forwardUrl);
   }

   /**
    * 如果是普通视图, 创建该视图视图
    */
   return super.createView(viewName, locale);
}

分析: 这里对视图名称进行解析, 如果视图名称是以" redirect: " 开头,则作为重定向处理; 如果视图是以" forward: "开头, 则作为请求转发处理; 否则就创建视图,并返回

AbstractCachingViewResolver#createView()方法实现:

@Nullable
protected View createView(String viewName, Locale locale) throws Exception {
   /**
    * {@link UrlBasedViewResolver#loadView(java.lang.String, java.util.Locale)}
    */
   return loadView(viewName, locale);
}

UrlBasedViewResolver#loadView()方法实现:

@Override
protected View loadView(String viewName, Locale locale) throws Exception {
   /**
    *  使用逻辑视图名按照指定规则生成View对象
    * {@link InternalResourceViewResolver#buildView(java.lang.String)}
    */
   AbstractUrlBasedView view = buildView(viewName);
   /**
    * 应用声明周期函数,也就是调用View对象的初始化函数和Spring用于切入bean创建的
    *  Processor和Aware函数
    */
   View result = applyLifecycleMethods(viewName, view);
   // 检查view的准确性,这里默认始终返回true
   return (view.checkResource(locale) ? result : null);
}


分析:

  1. 使用逻辑视图名按照指定规则生成View对象
  2. 应用声明周期函数,也就是调用View对象的初始化函数和Spring用于切入bean创建的Processor和Aware函数

(1)  InternalResourceViewResolver#buildView()方法实现:

@Override
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
   InternalResourceView view = (InternalResourceView) super.buildView(viewName);
   if (this.alwaysInclude != null) {
      view.setAlwaysInclude(this.alwaysInclude);
   }
   view.setPreventDispatchLoop(true);
   return view;
}

UrlBasedViewResolver#buildView()方法实现:

protected AbstractUrlBasedView buildView(String viewName) throws Exception {
   // 对于InternalResourceViewResolver而言,其返回的View对象的具体类型是InternalResourceView
   Class viewClass = getViewClass();
   Assert.state(viewClass != null, "No view class");
   // 使用反射生成InternalResourceView对象实例
   AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass);
   //根据前缀和后缀拼接视图路径信息
   view.setUrl(getPrefix() + viewName + getSuffix());
       // 设置View的contentType属性
   String contentType = getContentType();
   if (contentType != null) {
      view.setContentType(contentType);
   }
   // 设置contextAttribute和attributeMap等属性
   view.setRequestContextAttribute(getRequestContextAttribute());
   view.setAttributesMap(getAttributesMap());
   // pathVariables表示request请求url中的属性,这里主要是设置是否将这些属性暴露到视图中
   Boolean exposePathVariables = getExposePathVariables();
   if (exposePathVariables != null) {
      view.setExposePathVariables(exposePathVariables);
   }
   // 这里设置的是是否将Spring的bean暴露在视图中,以供给前端调用
   Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();
   if (exposeContextBeansAsAttributes != null) {
      view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
   }
   // 设置需要暴露给前端页面的bean名称
   String[] exposedContextBeanNames = getExposedContextBeanNames();
   if (exposedContextBeanNames != null) {
      view.setExposedContextBeanNames(exposedContextBeanNames);
   }

   return view;
}

(2) UrlBasedViewResolver#applyLifecycleMethods()方法实现:

protected View applyLifecycleMethods(String viewName, AbstractUrlBasedView view) {
   ApplicationContext context = getApplicationContext();
   if (context != null) {
      // 对生成的View对象应用初始化方法,主要包括InitializingBean.afterProperties()和一些
      // Processor,Aware方法
      Object initialized = context.getAutowireCapableBeanFactory().initializeBean(view, viewName);
      if (initialized instanceof View) {
         return (View) initialized;
      }
   }
   return view;
}

2. 根据View实例渲染视图

AbstractView#render()方法实现:

@Override
public void render(@Nullable Map model, HttpServletRequest request,
      HttpServletResponse response) throws Exception {

   if (logger.isTraceEnabled()) {
      logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +
         " and static attributes " + this.staticAttributes);
   }
   /**
    * 这里主要是将request中pathVariable,staticAttribute与用户返回的model属性
    * 合并为一个Map对象,以供给后面对视图的渲染使用
    */
   Map mergedModel = createMergedOutputModel(model, request, response);
   /**
    * 判断当前View对象的类型是否为文件下载类型,如果是文件下载类型,则设置response的
    * Pragma和Cache-Control等属性值
    */
   prepareResponse(request, response);
   /**
    * 开始view视图渲染以及数据输出整理  ==> 重点
    * {@link InternalResourceView#renderMergedOutputModel(java.util.Map, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
    */
   renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}

InternalResourceView#renderMergedOutputModel()方法实现:

@Override
protected void renderMergedOutputModel(
      Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {

   /**
    * 将Model中的键值对数据全部写进RequestScope中
    */
   exposeModelAsRequestAttributes(model, request);

   //提供的一个hook方法,默认是空实现,用于用户进行request属性的自定义使用
   exposeHelpers(request);

   // 确定请求分配器的路径
   String dispatcherPath = prepareForRendering(request, response);

   /**
    *  获取当前request的RequestDispatcher对象,该对象有两个方法:include()和forward(),
    *  用于对当前的request进行转发,其实也就是将当前的request转发到另一个url,这里的另一个
    *  url就是要解析的视图地址,也就是说进行视图解析的时候请求的对于文件的解析实际上相当于
    *  构造了另一个(文件)请求,在该请求中对文件内容进行渲染,从而得到最终的文件。这里的
    *  include()方法表示将目标文件引入到当前文件中,与jsp中的include标签作用相同;
    *  forward()请求则表示将当前请求转发到另一个请求中,也就是目标文件路径,这种转发并不会
    *  改变用户浏览器地址栏的请求地址。
    */
   RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
   if (rd == null) {
      throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
            "]: Check that the corresponding file exists within your web application archive!");
   }

   // 判断当前是否为include请求,如果是,则调用RequestDispatcher.include()方法进行文件引入
   if (useInclude(request, response)) {
      response.setContentType(getContentType());
      if (logger.isDebugEnabled()) {
         logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
      }
      rd.include(request, response);
   }
   /**
    * 请求转发
    *
    * 使用forward跳转则后面的response输出则不会执行,而用include来跳转,
    * 则include的servlet执行完后,再返回到原来的servlet执行response的输出(如果有)
    */
   else {
      // Note: The forwarded resource is supposed to determine the content type itself.
      if (logger.isDebugEnabled()) {
         logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
      }
      /**
       *  如果当前不是include()请求,则直接使用forward请求将当前请求转发到目标文件路径中,
       *     从而渲染该视图
       */
      rd.forward(request, response);
   }
}

至此,视图解析分析完成;

 

相关文章:

     SpringMVC源码解析一(在Spring源码项目中搭建SpringMVC源码模块)

     SpringMVC源码解析二(请求过程解析)

     SpringMVC源码解析三(处理映射器HandlerMapping的解析)

     SpringMVC源码解析四(处理适配器HandlerAdapter的解析)

     SpringMVC源码解析五(HandlerMethod执行过程解析)

     SpringMVC源码解析六(ModelAndView解析)

     SpringMVC源码解析七(初始化过程解析

你可能感兴趣的:(SpringMVC源码解析)