SpringMVC源码解析(三)——视图解析

源码解析
创建视图
 
一、 view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
    解析视图名 拿到视图
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();//根据ModelAndView mv的到视图名
   if (viewName != null) {
      // We need to resolve the view name.
      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 {
      // No need to lookup: the ModelAndView object contains the actual View object.
      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.isTraceEnabled()) {
      logger.trace("Rendering view [" + view + "] ");
   }
   try {
      if (mv.getStatus() != null) {
         response.setStatus(mv.getStatus().value());
      }
      view.render(mv.getModelInternal(), request, response);
   }
   catch (Exception ex) {
      if (logger.isDebugEnabled()) {
         logger.debug("Error rendering view [" + view + "]", ex);
      }
      throw ex;
   }
}
 一、解析视图名view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
视图解析器得到View对象的流程就是,所有配置的视图解析器都来尝试根据视图名(返回值)得到View(视图)对象;如果能得到就返回,得不到就换下一个视图解析器;
@Nullable
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
      Locale locale, HttpServletRequest request) throws Exception {
 
  if (this.viewResolvers != null) {
      for (ViewResolver viewResolver : this.viewResolvers) {//viewResolvers是onfresh初始化时xml中在自己配置的,也可以使用默认
          View view = viewResolver.resolveViewName(viewName, locale); 
         if (view != null) {
            return view;
         }
      }
   }
   return null;
}
@Override
@Nullable
public View resolveViewName(String viewName, Locale locale) throws Exception {
   if (!isCache()) {
      return createView(viewName, locale);
   }
   else {
      Object cacheKey = getCacheKey(viewName, locale);
      View view = this.viewAccessCache.get(cacheKey);
      if (view == null) {
         synchronized (this.viewCreationCache) {
            view = this.viewCreationCache.get(cacheKey);
            if (view == null) {
               // Ask the subclass to create the View object.
               view = createView(viewName, locale);//根据视图名和国际哈创建视图对象
               if (view == null && this.cacheUnresolved) {
                  view = UNRESOLVED_VIEW;
               }
               if (view != null && this.cacheFilter.filter(view, viewName, locale)) {
                  this.viewAccessCache.put(cacheKey, view);
                  this.viewCreationCache.put(cacheKey, view);
               }
            }
         }
      }
      else {
         if (logger.isTraceEnabled()) {
            logger.trace(formatKey(cacheKey) + "served from cache");
         }
      }
      return (view != UNRESOLVED_VIEW ? view : null);
   }
}
根据不同的前缀创建不同的视图
@Override
protected View createView(String viewName, Locale locale) throws Exception {
   // If this resolver is not supposed to handle the given view,
   // return null to pass on to the next resolver in the chain.
   if (!canHandle(viewName, locale)) {
      return null;
   }
 
 
   // Check for special "redirect:" prefix.
//以redirect:为前缀创建RedirectView视图对象
   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);
   }
 
 
   // Check for special "forward:" prefix.
//以forward:为前缀创建InternalResourceView视图对象
   if (viewName.startsWith(FORWARD_URL_PREFIX)) {
      String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
      InternalResourceView view = new InternalResourceView(forwardUrl);
      return applyLifecycleMethods(FORWARD_URL_PREFIX, view);
   }
 
 
   // Else fall back to superclass implementation: calling loadView.
如果没有前缀  就使用父类默认创建一个View
返回的是一个    InternalResourceView 详细看下面源码部分
   return super.createView(viewName, locale);
}

super.createView(viewName, locale);往深处挖到此方法

InternalResourceView view = (InternalResourceView) super.buildView(viewName);

的方法进入后

可以看出自动拼串

最后弹栈出来得到View
 
二、渲染视图view.render(mv.getModelInternal(), request, response);

1)调用View对象的render方法

@Override
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
 
 
    if (logger.isDebugEnabled()) {
        logger.debug("View " + formatViewName() +
            ", model " + (model != null ? model : Collections.emptyMap()) +
        (this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));
    }
 
 
    Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
    prepareResponse(request, response);
  ⚪ renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
 
2)InternalResourceView有这个方法renderMergedOutputModel;
 
@Override
protected void renderMergedOutputModel(
      Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
 
 
   // Expose the model object as request attributes.
    请求域中有隐含模型的数据  全在这个方法中
   exposeModelAsRequestAttributes(model, request);
(插)
// Expose helpers as request attributes, if any.
   exposeHelpers(request);
 
 
   // Determine the path for the request dispatcher.
    //拿到当前路径/WEB-INF/pages/success.jsp
   String dispatcherPath = prepareForRendering(request, response);
 
 
   // Obtain a RequestDispatcher for the target resource (typically a JSP).
    //就是拿到serlvet里规定的转发器RequestDispatcher
   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!");
   }
 
 
   // If already included or response already committed, perform include, else forward.
   if (useInclude(request, response)) {
      response.setContentType(getContentType());
      if (logger.isDebugEnabled()) {
         logger.debug("Including [" + getUrl() + "]");
      }
      rd.include(request, response);
   }
 
   else {
      // Note: The forwarded resource is supposed to determine the content type itself.
      if (logger.isDebugEnabled()) {
         logger.debug("Forwarding to [" + getUrl() + "]");
      }
      rd.forward(request, response);//请求转发
   }
}

3)将模型中的所有数据取出来全放在request域中

 

protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {

        for (Map.Entry<String, Object> entry : model.entrySet()) {

            String modelName = entry.getKey();

            Object modelValue = entry.getValue();

            if (modelValue != null) {

         //将ModelMap中的数据放到请求域中

                request.setAttribute(modelName, modelValue);

        if (logger.isDebugEnabled()) {

                    logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() +

                            "] to request in view with name '" + getBeanName() + "'");

                }

            }

            else {

                request.removeAttribute(modelName);

                if (logger.isDebugEnabled()) {

                    logger.debug("Removed model object '" + modelName +

                            "' from request in view with name '" + getBeanName() + "'");

                }

            }

        }

    }

总结:
视图解析器只是为了得到一个视图对象
视图对象才能真正转发(将隐含模型数据全部放到请求域中)或者重定向页面
 
 
 
 
 
 
 
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值