视图框架:Spring MVC 4.0(2)

《springMVC4(7)模型视图方法源码综合分析》一文中,我们介绍了ModelAndView的用法,它会在控制层方法调用完毕后作为返回值返回,里面封装好了我们的业务逻辑数据和视图对象或视图名 
。下一步,视图对象往往会对模型进一步渲染,再由视图解析器进一步解析并向前端发出响应。在下面,我们详细介绍视图和视图解析器的各种分类。 
在View接口中,定义了一个核心方法是:

void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
  • 1
  • 1

它的作用主要是渲染模型数据,整合web资源,并以特定形式响应给客户,这些形式可以是复杂JSP页面,也可以是简单的json、xml字符串。 
针对不同的响应形式,spring为我们设计了不同的View实现类:

针对不同的视图对象,我们使用不同的视图解析器来完成实例化工作。我们可以在spring上下文配置多个视图解析器,并通过order属性来指定他们之间的解析优先级顺序,order 越小,对应的 ViewResolver 将有越高的解析视图的权利。当一个 ViewResolver 在进行视图解析后返回的 View 对象是 null 的话就表示该 ViewResolver 不能解析该视图,这时候就交给优先级更低的进行解析,直到解析工作完成,如果所有视图解析器都不能完成将解析,则会抛出异常。 
类似于视图,Spring也为我们提供了众多的视图解析器实现类: 

Spring MVC 处理流程

重要接口和类介绍

1. View接口

  视图基础接口,它的各种实现类是无状态的,因此是线程安全的。 该接口定义了两个方法: 

2. AbstractView抽象类

  View接口的基础实现类。我们稍微介绍一下这个抽象类。

  首先看下这个类的属性:

  再看下抽象类中接口方法的实现:

  getContentType方法直接返回contentType属性即可。

  render方法:

3. AbstractUrlBasedView抽象类

  继承自AbstractView抽象类,增加了1个类型为String的url参数。

4. InternalResourceView类

  继承自AbstractUrlBasedView抽象类的类,表示JSP视图。

  我们看下这个类的renderMergedOutputModel方法(AbstractView抽象类定义的抽象方法,为View接口提供的render方法服务)。

5. JstlView类

  JSTL视图,继承自InternalResourceView,该类大致上与InternalResourceView类一致。

6. AbstractTemplateView抽象类

  继承自AbstractUrlBasedView抽象类,重写了renderMergedOutputModel方法,在该方法中会调用renderMergedTemplateModel方法,renderMergedTemplateModel方法为新定义的抽象方法。

  该抽象类有几个boolean属性exposeSessionAttributes,exposeRequestAttributes。 设置为true的话会将request和session中的键值和值丢入到renderMergedTemplateModel方法中的model这个Map参数中。

  这个类是某些模板引擎视图类的父类。 比如FreemarkerView,VelocityView。

7. FreeMarkerView类

  继承自AbstractTemplateView抽象类。

  直接看renderMergedTemplateModel方法,renderMergedTemplateModel内部会调用doRender方法:

8. RedirectView类

  继承自AbstractUrlBasedView,并实现SmartView接口。SmartView接口定义了1个boolean isRedirectView();方法。

  该视图的renderMergedOutputModel方法主要就是通过response.sendRedirect进行重定向。

  有关RedirectView方面的知识楼主另外写了1篇博客。http://www.cnblogs.com/fangjian0423/p/springMVC-redirectView-analysis.html

9. ViewResolver接口

  视图解释器,用来解析视图View,与View接口配合使用。

  该接口只有1个方法,通过视图名称viewName和Locale对象得到View接口实现类:

  View resolveViewName(String viewName, Locale locale) throws Exception;

10. AbstractCachingViewResolver抽象类

  带有缓存功能的ViewResolver接口基础实现抽象类,该类有个属性名为viewAccessCache的以 "viewName_locale" 为key, View接口为value的Map。

  该抽象类实现的resolveViewName方法内部会调用createView方法,方法内部会调用loadView抽象方法。

11. UrlBasedViewResolver类

  继承自AbstractCachingViewResolver抽象类、并实现Ordered接口的类,是ViewResolver接口简单的实现类。

  该类复写了createView方法:

  父类(AbstractCachingViewResolver)的createView方法内部会调用loadView抽象方法,UrlBasedViewResolver实现了这个抽象方法:

  下面对UrlBasedViewResolver做1个test,配置如下:

<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
  <property name="prefix" value="/WEB-INF/view/"/>
  <property name="suffix" value=".jsp"/>
  <property name="viewClass" value="org.springframework.web.servlet.view.InternalResourceView"/>
  <property name="viewNames">
    <array>
      <value type="java.lang.String">*</value>
    </array>
  </property>
  <property name="contentType" value="text/html;charset=utf-8"/>
  <property name="attributesMap">
    <map>
      <entry key="mytest" value="mytestvalue"/>
    </map>
  </property>
  <property name="attributes">
    <props>
      <prop key="test">testvalue</prop>
    </props>
  </property>
</bean>

  我们看到:以InternalResourceView这个JSP视图作为视图;viewNames我们设置了*,这里的*代表全部视图名(这个viewNames属性不设置也可以,代表全部视图名都处理);http响应头部contentType信息:text/html;charset=utf-8;attributesMap和attributes传入的Map和Properties参数都会被丢入到staticAttributes属性中,这个staticAttributes会被设置成AbstractView的staticAttributes属性,也就是request域中的参数。

  我们看到request域中没有设置mytest和testvalue值。但是页面中会显示,因为我们配置了attributesMap和attributes参数。

  如果我们把viewNames中的"*"改成"index1"。那么就报错了,因为处理视图名的时候index匹配不上index1。

12. InternalResourceViewResolver类

  继承自UrlBasedViewResolver,以InternalResourceView作为视图,若项目中存在“javax.servlet.jsp.jstl.core.Config”该类,那么会以JstlView作为视图。重写了buildView方法,主要就是为了给InternalResourceView视图设置属性。

13. AbstractTemplateViewResolver类

  继承自UrlBasedViewResolver,重写了buildView方法,主要就是构造AbstractTemplateView以及为它设置相应的属性。

14. FreeMarkerViewResolver类

  继承自AbstractTemplateViewResolver,将视图设置为FreeMarkerView。

15. ModelAndView对象

  顾名思义,带有视图和Model属性的一个模型和视图类。

  值得注意的是,这个视图属性是一个Object类型的数据,可以直接是View接口的实现类或者视图名(字符串)。

源码分析

下面我们来分析SpringMVC处理视图的源码。

SpringMVC在处理请求的时候,通过RequestMappingHandlerMapping得到HandlerExecutionChain,然后通过RequestMappingHandlerAdapter得到1个ModelAndView对象,之后通过processDispatchResult方法处理。

processDispatchResult方法如下:

如果配置的ViewResolver如下:

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="prefix" value="/WEB-INF/view/"/>
  <property name="suffix" value=".jsp"/>
</bean>

那么就是使用InternalResourceViewResolver来解析视图。

之前分析过,InternalResourceViewResolver重写了UrlBasedViewResolver的buildView方法。但是还是会调用UrlBasedViewResolver的buildView方法。

最终得到InternalResourceView或JstlView视图。这两个视图的render方法本文介绍重要接口及类的时候已分析。

PS:DispathcerServlet中的viewResolvers属性是个集合,如果存在多个ViewResolver对象,必定会有优先级的问题,这部分的内容请参考楼主的另一篇博客:

http://www.cnblogs.com/fangjian0423/p/spring-Ordered-interface.html

编码自定义的ViewResolver

下面,我们就来编写自定义的ViewResolver。

自定义的ViewResolver处理视图名的时候,遇到 "jsp:" 开头的会找jsp页面,遇到 "freemarker:" 开头的找freemarker页面。

public class CustomViewResolver extends UrlBasedViewResolver {

  public static final String JSP_URL_PREFIX = "jsp:";
  public static final String FTL_URL_PREFIX = "freemarker:";

  private static final boolean jstlPresent = ClassUtils.isPresent(
            "javax.servlet.jsp.jstl.core.Config", CustomViewResolver.class.getClassLoader());

  private Boolean exposePathVariables = false;

  private boolean exposeRequestAttributes = false;

  private boolean allowRequestOverride = false;

  private boolean exposeSessionAttributes = false;

  private boolean allowSessionOverride = false;

  private boolean exposeSpringMacroHelpers = true;

  public CustomViewResolver() {
      this.setViewClass(FreeMarkerView.class);
  }  

  @Override
  protected AbstractUrlBasedView buildView(String viewName) throws Exception {
    if(viewName.startsWith(FTL_URL_PREFIX)) {
        return buildFreemarkerView(viewName.substring(FTL_URL_PREFIX.length()));
    } else if(viewName.startsWith(JSP_URL_PREFIX)) {
        Class viewCls = jstlPresent ? JstlView.class : InternalResourceView.class;
        return buildView(viewCls, viewName.substring(JSP_URL_PREFIX.length()), getPrefix(), ".jsp");
    } else {
        //默认以freemarker处理
        return buildFreemarkerView(viewName);
    }
  }

  private AbstractUrlBasedView build(Class viewClass, String viewName, String prefix, String suffix) {
    AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass);
    view.setUrl(prefix + viewName + suffix);
    String contentType = getContentType();
    if (contentType != null) {
        view.setContentType(contentType);
    }
    view.setRequestContextAttribute(getRequestContextAttribute());
    view.setAttributesMap(getAttributesMap());
    if (this.exposePathVariables != null) {
        view.setExposePathVariables(exposePathVariables);
    }
    return view;
  }

  private AbstractUrlBasedView buildFreemarkerView(String viewName) throws Exception {
    AbstractTemplateView view = (AbstractTemplateView) build(FreeMarkerView.class, viewName, "", getSuffix());
    view.setExposeRequestAttributes(this.exposeRequestAttributes);
    view.setAllowRequestOverride(this.allowRequestOverride);
    view.setExposeSessionAttributes(this.exposeSessionAttributes);
    view.setAllowSessionOverride(this.allowSessionOverride);
    view.setExposeSpringMacroHelpers(this.exposeSpringMacroHelpers);
    return view;
  }

  //get set方法省略

}

xml配置:

<bean class="org.format.demo.support.viewResolver.CustomViewResolver">
  <property name="prefix" value="/WEB-INF/view/"/>
  <property name="suffix" value=".ftl"/>
  <property name="contentType" value="text/html;charset=utf-8"/>
  <property name="exposeRequestAttributes" value="true"/>
  <property name="exposeSessionAttributes" value="true"/>
  <property name="exposeSpringMacroHelpers" value="true"/>
  <property name="requestContextAttribute" value="request"/>
</bean>
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
  <property name="templateLoaderPath" value="/WEB-INF/view/"/>
  <property name="defaultEncoding" value="utf-8"/>
  <property name="freemarkerSettings">
    <props>
      <prop key="template_update_delay">10</prop>
      <prop key="locale">zh_CN</prop>
      <prop key="datetime_format">yyyy-MM-dd</prop>
      <prop key="date_format">yyyy-MM-dd</prop>
      <prop key="number_format">#.##</prop>
    </props>
  </property>
</bean>

简单解释一下:CustomViewResolver解析视图名的时候,判断 "jsp:" 和 "freemarker:" 开头的名字,如果是 "jsp:" 开头的,如果有JSTL依赖,构造JSTLView视图,否则构造InternalResourceView视图。如果是 "freemarker:" 构造FreemarkerView。在构造视图之前分别会设置一些属性。

xml配置:配置prefix是为了给jsp视图用的,freemarker视图不需要prefix,因为FreemarkerView内部会使用配置的FreeMarkerConfigurer,并用FreeMarkerConfigurer内部的templateLoaderPath属性作为前缀,配置的suffix是为了让FreemarkerView使用,当后缀。

最后附上Controller代码:

@Controller
@RequestMapping(value = "/tvrc")
public class TestViewResolverController {

    @RequestMapping("jsp")
    public ModelAndView jsp(ModelAndView view) {
        view.setViewName("jsp:trvc/index");
        return view;
    }

    @RequestMapping("/ftl")
    public ModelAndView freemarker(ModelAndView view) {
        view.setViewName("freemarker:trvc/index");
        return view;
    }

}

视图 /WEB-INF/view/trvc/index.jsp 中的的内容是输出<h2>This is jsp page</h2>

视图 /WEB-INF/view/trvc/index.ftl 中的的内容是输出<h2>This is freemarker page</h2>

总结

本文分析了SpringMVC中的视图机制,View和ViewResolver这两个接口是视图机制的核心,并分析了几个重要的View和ViewResolver接口实现类,最终写了一个区别jsp和freemarker视图的ViewResolver实现类,让读者更加理解视图机制。

时间: 2024-10-09 20:46:55

视图框架:Spring MVC 4.0(2)的相关文章

Spring MVC 3.0.5+Spring 3.0.5+MyBatis3.0.4全注解实例详解(二)

在上一篇文章中我详细的介绍了如何搭建maven环境以及生成一个maven骨架的web项目,那么这章中我将讲述Spring MVC的流程结构,Spring MVC与Struts2的区别,以及例子中的一些配置文件的分析. 一.Spring MVC 3.0介绍 Spring MVC是一个典型的MVC框架,是Spring内置的Web框架,可以作为应用项目的展示层,继Spring 2.0对Spring MVC进行重大升级后,Spring 2.5又为Spring MVC引入了注解驱动功能,再到3.0时代,全

Spring MVC 3.0.5+Spring 3.0.5+MyBatis3.0.4全注解实例详解(四)

这一章大象将详细分析web层代码,以及使用Spring MVC的注解及其用法和其它相关知识来实现控制器功能.     之前在使用Struts2实现MVC的注解时,是借助struts2-convention这个插件,如今我们使用Spring自带的spring-webmvc组件来实现同样的功能,而且比之以前更简单.另外,还省掉了整合两个框架带来的不稳定因素.     对于Spring MVC框架,我主要讲一下它的常用注解,再结合一些示例进行说明,方便大家能够快速理解.     一.Spring MV

Spring MVC 3.0.5+Spring 3.0.5+MyBatis3.0.4全注解实例详解(五)

这是本系列的最后一篇,主要讲一下FreeMarker模板引擎的基本概念与常用指令的使用方式.     一.FreemMarker基本概念     FreemMarker是一个用Java语言编写的模板引擎,它是一个基于模板来生成文本输出的一个工具.是除了JSP之外被使用得最多的页面模板技术之一,另一个比较有名的模板则是Velocity.     用户可以使用FreeMarker来生成所需要的内容,通常由Java提供数据模型,FreeMarker通过模板引擎渲染数据模型,这样最终得到我们想要的内容.

java 使用框架Spring MVC批量上传图片

java 使用框架Spring MVC批量上传图片 运行环境:jdk1.8.0_17+tomcat 8 + spring:4.3.4+mybatis:3.2.7+ eclipse ide 2018+maven管理 最近写项目时候用到上传图片文件等功能之前写过没有总结,以及记录等,今天总结下写的批量上传图片功能 前段只需要把所有的选择的图片提交上来即可,后台会逐个处理,并且会在服务器或者当前电脑自己新建一个存放图片的文件夹,在application.properties配置里面你可以指定你所存放图

Spring MVC 3.0.5+Spring 3.0.5+MyBatis3.0.4全注解实例详解(三)

前两章我为大家详细介绍了如何搭建Maven环境.Spring MVC的流程结构.Spring MVC与Struts2的区别以及示例中的一些配置文件的分析.在这一章,我就对示例的层次结构进行说明,以及MyBatis的一些简单介绍. 本文不会对MyBatis作详细说明,大象还是假定阅读本文的朋友对MyBatis(ibatis)有最基本的了解,只有这样才能较好的理解本文的内容.关于MyBatis请查看它的官方文档及其它参考资料,本文不作详细讨论. 一.工程结构图      上面这是典型的Maven项目

Spring MVC 3.0.5+Spring 3.0.5+MyBatis3.0.4全注解实例详解(一)

Spring更新到3.0之后,其MVC框架加入了一个非常不错的东西——那就是REST.它的开放式特性,与Spring的无缝集成,以及Spring框架的优秀表现,使得现在很多公司将其作为新的系统开发框架.大象根据实际的项目经验,以之前SSH2例子为基础,对其进行一次大改造,详细的为大家讲解如何实现SSM3全注解式的开发. 这次大象将采取两种构建方式,一是很多人喜欢用的MyEclipse,另一个,则是用Eclipse+Maven.这一篇,将主要讲解开发环境设置与Maven构建方式. 1. 开发环境

CRUD using Spring MVC 4.0 RESTful Web Services and AngularJS

原文: http://www.tuicool.com/articles/MneeU3f Based on the requests from many readers, I am now presenting an article on how to make CRUD operations using Spring MVC 4.0 RESTFul web services and AngularJS. I had already written few articles on Spring M

视图框架:Spring MVC 4.0(1)

目录 一.表单标签库 1.1.简介 1.2.常用属性 1.3.form标签与input标签 1.4.checkbox标签 1.5.radiobutton标签 1.6.password标签 1.7.select标签 1.8.option标签 1.9.options标签 1.10.textarea.errors标签 1.11.hidden标签 1.12.radiobuttons 单选列表与checkboxs复选列表 二.视图解析器 2.1.ViewResolver视图解析器 2.2.链式视图解析器

Spring MVC 3.0简介

1.    背景介绍 Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块.使用 Spring 可插入的 MVC 架构,可以选择是使用内置的 Spring Web 框架还是 Struts 这样的 Web 框架.通过策略接口,Spring 框架是高度可配置的,而且包含多种视图技术,例如 JavaServer Pages(JSP)技术.Velocity.Tiles.iText 和 POI.Spring MVC 框架并不知道使用的视图,所以不会强迫您只使用 JSP 技术.Spring