返回介绍

11.4.9 根据视图跳转页面

发布于 2025-04-22 22:09:18 字数 9304 浏览 0 评论 0 收藏

无论是一个系统还是一个站点,最重要的工作都是与用户进行交互,用户操作系统后无论下发的命令成功与否都需要给用户一个反馈,以便于用户进行下一步的判断。所以,在逻辑处理的最后一定会涉及一个页面跳转的问题。

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.resolveLocale(request);

 response.setLocale(locale);

  View view;

  if (mv.isReference()) {

   // We need to resolve the view name.

   view =resolveViewName(mv.getViewName(), 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.isDebugEnabled()) {

   logger.debug("Rendering view [" + view + "] in DispatcherServlet with name

   '" + getServletName() + "'");

 }

  view.render(mv.getModelInternal(), request, response);

}

1.解析视图名称

在上文中我们提到 DispatcherServlet 会根据 ModelAndView 选择合适的视图来进行渲染,而这一功能就是在 resolveViewName 函数中完成的。

protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,

HttpServletRequest request) throws Exception {

  for (ViewResolver viewResolver : this.viewResolvers) {

   View view = viewResolver.resolveViewName(viewName, locale);

   if (view != null) {

    return view;

  }

 }

  return null;

}

我们以 org.Springframework.web.servlet.view.InternalResourceViewResolver 为例来分析 ViewResolver 逻辑的解析过程,其中 resolveViewName 函数的实现是在其父类 AbstractCaching ViewResolver 中完成的。

public View resolveViewName(String viewName, Locale locale) throws Exception {

  if (!isCache()) {

  //不存在缓存的情况下直接创建视图

   return createView(viewName, locale);

 }

  else {

  //直接从缓存中提取

   Object cacheKey = getCacheKey(viewName, locale);

   synchronized (this.viewCache) {

    View view = this.viewCache.get(cacheKey);

    if (view == null && (!this.cacheUnresolved || !this.viewCache.

    containsKey(cacheKey))) {

     // Ask the subclass to create the View object.

     view = createView(viewName, locale);

     if (view != null || this.cacheUnresolved) {

      this.viewCache.put(cacheKey, view);

      if (logger.isTraceEnabled()) {

       logger.trace("Cached view [" + cacheKey + "]");

     }

    }

   }

    return view;

  }

 }

}

在父类 UrlBasedViewResolver 中重写了 createView 函数。

protected View createView(String viewName, Locale locale) throws Exception {

 //如果当前解析器不支持当前解析器如 viewName 为空等情况

  if (!canHandle(viewName, locale)) {

   return null;

 }

 //处理前缀为 redirect:xx 的情况

  if (viewName.startsWith(REDIRECT_URL_PREFIX)) {

   String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());

   RedirectView view = new RedirectView(redirectUrl, isRedirectContext

   Relative(), isRedirectHttp10Compatible());

   return applyLifecycleMethods(viewName, view);

 }

//处理前缀为 forward:xx 的情况

  if (viewName.startsWith(FORWARD_URL_PREFIX)) {

   String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());

   return new InternalResourceView(forwardUrl);

 }

  // Else fall back to superclass implementation: calling loadView.

  return super.createView(viewName, locale);

}

protected View createView(String viewName, Locale locale) throws Exception {

  return loadView(viewName, locale);

}

protected View loadView(String viewName, Locale locale) throws Exception {

  AbstractUrlBasedView view = buildView(viewName);

  View result = applyLifecycleMethods(viewName, view);

  return (view.checkResource(locale) ? result : null);

}

protected AbstractUrlBasedView buildView(String viewName) throws Exception {

  AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass

 (getViewClass());

//添加前缀以及后缀

  view.setUrl(getPrefix() + viewName + getSuffix());

  String contentType = getContentType();

  if (contentType != null) {

  //设置 ContentType

  view.setContentType(contentType);

 }

 view.setRequestContextAttribute(getRequestContextAttribute());

 view.setAttributesMap(getAttributesMap());

  if (this.exposePathVariables != null) {

  view.setExposePathVariables(exposePathVariables);

 }

  return view;

}

通读以上代码,我们发现对于 InternalResourceViewResolver 所提供的解析功能主要考虑到了几个方面的处理。

基于效率的考虑,提供了缓存的支持。

提供了对 redirect:xx 和 forward:xx 前缀的支持。

添加了前缀及后缀,并向 View 中加入了必需的属性设置。

2.页面跳转

当通过 viewName 解析到对应的 View 后,就可以进一步地处理跳转逻辑了。

public void render(Map<String, ?> 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);

 }

  Map<String, Object> mergedModel = createMergedOutputModel(model, request,

 response);

  prepareResponse(request, response);

 renderMergedOutputModel(mergedModel, request, response);

}

在引导示例中,我们了解到对于 ModelView 的使用,可以将一些属性直接放入其中,然后在页面上直接通过 JSTL 语法或者原始的 request 获取。这是一个很方便也很神奇的功能,但是实现却并不复杂,无非是把我们将要用到的属性放入 request 中,以便在其他地方可以直接调用,而解析这些属性的工作就是在 createMergedOutputModel 函数中完成的。

protected Map<String, Object> createMergedOutputModel(Map<String, ?> model, HttpServletRequest

request,

  HttpServletResponse response) {

@SuppressWarnings("unchecked")

Map<String, Object> pathVars = this.exposePathVariables ?

  (Map<String, Object>) request.getAttribute(View.PATH_VARIABLES) : null;

// Consolidate static and dynamic model attributes.

int size = this.staticAttributes.size();

size += (model != null) ? model.size() : 0;

size += (pathVars != null) ? pathVars.size() : 0;

Map<String, Object> mergedModel = new HashMap<String, Object>(size);

mergedModel.putAll(this.staticAttributes);

if (pathVars != null) {

 mergedModel.putAll(pathVars);

}

if (model != null) {

 mergedModel.putAll(model);

}

// Expose RequestContext?

  if (this.requestContextAttribute != null) {

   mergedModel.put(this.requestContextAttribute, createRequestContext(request,

   response, mergedModel));

 }

  return mergedModel;

}

//处理页面跳转

protected void renderMergedOutputModel(

  Map<String, Object> model, HttpServletRequest request, HttpServletResponse

  response) throws Exception {

  // Determine which request handle to expose to the RequestDispatcher.

  HttpServletRequest requestToExpose = getRequestToExpose(request);

//将 model 中的数据以属性的方式设置到 request 中

  exposeModelAsRequestAttributes(model, requestToExpose);

  // Expose helpers as request attributes, if any.

 exposeHelpers(requestToExpose);

  // Determine the path for the request dispatcher.

  String dispatcherPath = prepareForRendering(requestToExpose, response);

  // Obtain a RequestDispatcher for the target resource (typically a JSP).

  RequestDispatcher rd = getRequestDispatcher(requestToExpose, 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(requestToExpose, response)) {

  response.setContentType(getContentType());

   if (logger.isDebugEnabled()) {

    logger.debug("Including resource [" + getUrl() + "] in Internal

    ResourceView '" + getBeanName() + "'");

  }

   rd.include(requestToExpose, response);

 }

  else {

   // Note: The forwarded resource is supposed to determine the content type itself.

  exposeForwardRequestAttributes(requestToExpose);

   if (logger.isDebugEnabled()) {

    logger.debug("Forwarding to resource [" + getUrl() + "] in

    InternalResourceView '" + getBeanName() + "'");

  }

   rd.forward(requestToExpose, response);

 }

}

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。