• spring mvc 视图定位器ViewResolver


    我们已经知道了ViewResolver的主要职责是,根据Controller所返回的ModelAndView中的逻辑视图名,为DispatcherServlet返回一个可用的View实例。现在是揭开ViewResolver如何“尽职”的时候了。

    有ViewResolver的职责为前提,理解甚至于自己声明一个ViewResolver接口变得不再困难。实际上ViewResolver接口定义确实很简单,如下所示:

    public interface ViewResolver {

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

    }

    接口实现类只需要根据resolveViewName()方法中以参数形式传入的逻辑视图名(viewName)和当前Locale的值,返回相应的View实例即可[17]。至于每个ViewResolver实现类如何处理具体的逻辑视图名与具体的View实例之间的对应关系,则因实现类的不同而存在差异。

    大部分的ViewResolver实现类,除了org.springframework.web.servlet.view.BeanName- ViewResolver是直接实现ViewResolver接口,都直接或者间接继承自org.springframe work. web.servlet.view.AbstractCachingViewResolver。因为针对每次请求都重新实例化View将可能为Web应用程序带来性能上的损失,所以Spring MVC在AbstractCachingViewResolver这一继承层次加入了View实例的缓存功能。AbstractCachingViewResolver默认启用View的缓存功能。对于生产环境来说,这是合理的默认值。不过,如果在测试或者开发环境下,我们想即刻反映相应的修改结果,可以通过setCache(false)暂时关闭AbstractCachingViewResolver的缓存功能。

    Spring MVC在AbstractCachingViewResolver的基础上为我们提供了一系列的ViewResolver实现。下面让我们来认识一下它们的庐山真面目。

    24.4.1 可用的ViewResolver实现类

    为了便于理解,我们可以将Spring MVC提供的ViewResolver划分为两类,一类称为“面向单一视图类型的ViewResolver,另一类则称为面向多视图类型的ViewResolver。下面是这两类ViewResolver的详细情况。

    1. 面向单一视图类型的ViewResolver

    该类别ViewResolver的正宗名称应该是UrlBasedViewResolver(它们都直接地或者间接地继承自该类)。使用该类别的ViewResolver,我们不需要为它们配置具体的逻辑视图名到具体View的映射关系。通常只要指定一下视图模板所在的位置,这些ViewResolver就会按照逻辑视图名,抓取相应的模板文件、构造对应的View实例并返回。之所有又将它们称之为面向单一视图类型的ViewResolver,是因为该类别中,每个具体的ViewResolver实现都只负责一种View类型的映射,ViewResolver与View之间的关系是一比一。比如,我们之前一直使用的InternalResourceView- Resolver,它通常就只负责到指定位置抓取JSP模板文件,并构造InternalResourceView类型的View实例并返回。而VelocityViewResolver则只关心指定位置的Velocity模板文件(.vm),并会将逻辑视图名映射到视图模板的文件名,然后构造VelocityView类型的View实例返回,诸如此类。

    属于该类别的主要ViewResolver实现类为如下几个。

    q InternalResourceViewResolver。它是我们使用最多的ViewResolver实现类型,它对应InternalResourceView[18]视图类型的映射,说白了也就是处理JSP模板类型的视图映射。如果DispatcherServlet在初始化的时候,不能在自己的WebApplicationContext中找到至少一个ViewResolver,那么,InternalResourceViewResolver将作为默认的ViewResolver被使用。

    q FreeMarkerViewResolver/VelocityViewResolver。FreeMarkerViewResolver和Velo- cityViewResolver分别负责对应FreeMarkerView和VelocityView类型视图的查找工作,它们将根据逻辑视图名到指定的位置获取对应的模板文件,并构造FreeMarkerView和VelocityView的实例返回给DispatcherServlet使用。

    q JasperReportsViewResolver。JasperReportsViewResolver只关心根据逻辑视图名到指定位置查找JasperReport类型模板文件,并返回AbstractJasperReportsView的具体子类型View实例,例如JasperReportsCsvView或者JasperReportsHtmlView等。

    q XsltViewResolver。只负责根据逻辑视图名查找并返回XsltView类型的View实例。

    启用以上这些ViewResolver,与使用InternalResourceViewResolver一样简单。最基本的方法是,使用prefix属性指定模板所在路径,使用suffix属性指定模板文件的后缀名。这样,在获取逻辑视图名之后,相应的ViewResolver内部就能够根据[prefix]+viewName+[suffix]这样的URL找到对应的模板文件,并构造对应的View实例而返回了。以VelocityViewResolver的使用为例,至于其他的几个ViewResolver的使用,你基本上就可以“举一反三”了,更加详尽的配置项,可以参考对应类的Javadoc或者Professional Java Development with the Spring Framework一书中对应视图章节的介绍内容。下面给出了针对VelocityViewResolver的配置代码示例:

    class="org.springframework.Web.servlet.view.velocity.VelocityViewResolver">

       

       

    现在DispatcherServlet对视图的请求将会由VelocityViewResolver接管,Velocity- ViewResolver将根据传入的逻辑视图名,到指定目录下查找.vm类型的Velocity模板文件,并构造VelocityView实例返回给DispatcherServlet使用。就跟我们所说的那样,它只负责到指定位置查找对应Velocity的单一视图类型,而不会返回其他,比如Freemarker视图对应的View实例。

    注意 关于使用Velocity作为视图技术需要附加的配置内容,可以参考稍后ResourceBundle- ViewResolver部分的附带信息。

    2. 面向多视图类型的ViewResolver

    使用面向单一视图类型的ViewResolver,我们不需要指定明确的逻辑视图名与具体视图之间的映射关系,对应的ViewResolver将自动到指定位置匹配自己所管辖的那种视图模板,并构造具体的View实例。面向多视图类型的ViewResolver则不然。使用面向多视图类型的ViewResolver,我们需要通过某种配置方式明确指定逻辑视图名与具体视图之间的映射关系,这可能带来配置上的烦琐。不过,好处是,面向多视图类型的ViewResolver可以顾及多种视图类型的映射管理。如果你的逻辑视图名想要映射到InternalResourceView,那么面向多视图类型的ViewResolver可以做到。如果你的逻辑视图名想要映射到VelocityView,那么,面向多视图类型的ViewResolver也可以做到。相对于只支持单一视图类型映射的情况,面向多视图类型的ViewResolver更加灵活。

    面向多视图类型的ViewResolver的主要实现类有三个,它们分别是ResourceBundleView- Resolver、XmlViewResolver以及BeanNameViewResolver。以下是它们的详细情况介绍。

    ResourceBundleViewResolver。ResourceBundleViewResolver构建在ResourceBundle上,继承了ResourceBundle国际化支持的能力,也是所有的ViewResolver实现类中唯一提供视图国际化支持的ViewResolver。ResourceBundleViewResolver管理的视图的逻辑名称与具体视图的映射关系保存在properties文件中,格式符合Spring的IoC容器的properties配置格式。ResourceBundleView- Resolver内部将通过PropertiesBeanDefinitionReader加载这些配置信息。之后,根据逻辑视图名查找的操作,实际上也就简化为beanfactory.getBean(viewName)的形式了(当然,实际上要做的事情会多一些)。

    使用ResourceBundleViewResolver之前,我们得先将其添加到DispatcherServlet的WebApp- licationContext中,如下所示:

    class="org.springframework.Web.servlet.view.ResourceBundleViewResolver">

    如果我们没有指定properties配置文件从何处加载的话,ResourceBundleViewResolver默认将从classpath的根路径加载以views为basename的properties文件,比如views.properties、views_zh_CN.properties等。如果我们想改变这种默认加载行为,可以通过setBasename (String)或者setBasenames(String[])方法来进行变更。

    以下是一个典型的ResourceBundleViewResolver使用的properties配置文件内容:

    viewTemplate.class=org.springframework.Web.servlet.view.InternalResourceView

    viewTemplate.(abstract)=true

    help/HelpForSomething.(parent)=viewTemplate

    help/HelpForSomething.url=/WEB-INF/jsp/help/HelpForSomething.jsp

    hello.class=org.springframework.Web.servlet.view.velocity.VelocityView

    hello.url=cn/spring21/simplefx/resources/velocity/hello.vm

    # 其他视图定义……

    视图的bean定义主要有两个属性:class和url。如果我们想要避免每次为同一类型的视图指定某些共同的属性,也可以定义一个模板声明,然后通过parent引用该模板声明。这些特性在第二部分中我们已经领教过了,不是吗?

    注意 如果要在ResourceBundleViewResolver中使用Velocity或者Freemarker之类的通用模板引擎渲染的视图,那么需要在WebApplicationContext中添加相应的配置,使得视图渲染阶段能够获取模板引擎的支持。实际上,单独使用VelocityViewResolver或者Freemarker- ViewResolver也需要同样的配置。

    我们以使用Velocity类型视图的配置为例,(Freemarker类型视图的配置与Velocity类型视图的配置雷同)。在应用程序的WebApplicationContext中,我们添加org.springframework.web. servlet.view. velocity.VelocityConfigurer的配置如下:

       

        class="org.springframework.Web.servlet.view.velocity.VelocityConfigurer">

           

                value="/WEB-INF/velocity-config.properties"/>

        /bean>

    这样,在视图渲染阶段就可以根据该配置获取一个VelocityEngine进行视图模板与数据的合并(Merge)操作,以便最终输出视图页面。

    velocity-config.properties的配置内容,完全就是特定于Velocity的内容了。你可以参考Velocity的相关文档获取配置参数,这里可以给出一个简单的实例,如下所示:

    resource.loader = classpath

        classpath.resource.loader.description = Classpath Resource Loader

        classpath.resource.loader.class =

        org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader

        classpath.resource.loader.path=.

        velocimacro.library =

    最后,对于Velocity(或者Freemarker)的模板文件,最好像我们给出的配置内容所指定的那样,将它们放入应用程序的classpath中进行加载,而不是依赖于默认的文件系统加载行为。

    XmlViewResolver。XmlViewResolver与ResourceBundleViewResolver之间最主要的区别就是,它们所采用的配置文件格式不同。ResourceBundleViewResolver按照Spring IoC容器所接受的properties配置格式配置逻辑视图名与具体视图之间的映射关系,而XmlViewResolver则是按照Spring IoC容器接受的XML配置文件格式来加载映射信息。与ResourceBundleViewResolver同样的配置信息,使用XmlViewResolver的话,内容如代码清单24-28所示。

    代码清单24-28 XmlViewResolver使用配置代码示例

    class="org.springframework.Web.servlet.view.InternalResourceView"

    abstract="true">

       

    class="org.springframework.Web.servlet.view.velocity.VelocityView"

    p:url="cn/spring21/simplefx/resources/velocity/hello.vm">

    XmlViewResolver默认会加载/WEB-INF/views.xml作为配置文件。不过,我们可以在将XmlView- Resolver添加到WebApplicationContext的时候,根据情况改变这一默认行为,例如:

       

    现在,XmlViewResolver将从Classpath的根路径加载名为views.xml的配置文件。至于其他配置,比如Velocity需要的VelocityConfigurer,因为与使用何种ViewResolver没有关系,只与是否使用Velocity作为视图技术有关,所以依然需要根据情况添加到WebApplicationContext中。

    注意 XmlViewResolver并不支持视图的国际化(I18n)。如果必须对国际化视图给予支持,需要使用ResourceBundleViewResolver。

    BeanNameViewResolver。BeanNameViewResolver可以认为是XmlViewResolver的原型版或者简化版。使用它,我们可以直接将View实例注册到当前DispatcherServlet所使用的特定的WebAppli- cationContext中,而不用像XmlViewResolver那样另辟一块地。不过,BeanNameViewResolver更多地用于快速搭建应用框架原型,或者构建小型的Web应用程序。对于正常的基于Spring MVC的Web应用程序,应尽量避免将可以分离出来的视图配置信息一并加入到DispatcherServlet的WebApp- licationContext中。

    至于如何启用BeanNameViewResolver作为ViewResolver,我想你现在要比我清楚,如下所示:

    class="org.springframework.Web.servlet.view.BeanNameViewResolver"/>

    至于具体视图的配置,参照XmlViewResolver即可。

    实际上,正如我们所看到的那样,这三种ViewResolver在本质上是一样的,只不过是配置的表现形式上存在差异而已。最终的配置信息都将转换为Spring IoC容器中管理的View实例,BeanName- ViewResolver应该是最初的实现原型吧!

    24.4.2 ViewResolver查找序列(Chain Of ViewResolver)

    虽然我们在之前的示例中一直都是使用一个InternalResourceViewResolver进行视图查找,但这并不意味着每个基于Spring MVC的Web应用程序只能使用一个ViewResolver。实际上,Dispatcher- Servlet不但可以接受多个HandlerMapping以处理Web请求到具体Handler的映射,也可以接受多个ViewResolver以处理视图的查找。

    DispatcherServlet初始化时,将根据类型扫描自己的WebApplicationContext中定义的ViewResolver。如果查找到存在多个ViewResolver的定义,DispatcherServlet将根据这些ViewResolver的优先级进行排序,然后当需要根据逻辑视图名查找具体的View实例的时候,将按照排序后的顺序遍历这些ViewResolver,只要期间任何一个ViewResolver返回非空的View实例,当前查找即告结束。如果DispatcherServlet没能在当前的WebApplicationContext中找到任何的ViewResolver定义,它将使用InternalResourceViewResolver作为默认的ViewResolver使用。

    ViewResolver的优先级的指定使用Ordered接口作为标准,这已经成为Spring框架内设定优先级方式的惯例了。假设我们希望主要使用ResourceBundleViewResolver进行逻辑视图名到具体View实例的查找,如果没能找到,再寻求InternalResourceViewResolver的帮助。我们可以在Dispatcher- Servlet的WebApplicationContext中添加如下配置内容:

    class="org.springframework.Web.servlet.view.ResourceBundleViewResolver">

       

    class="org.springframework.Web.servlet.view.InternalResourceViewResolver">

       

       

    相应ViewResolver的bean定义对应的id或者name属性值是任意的,DispatcherServlet将按照类型来获取ViewResolver。如果没有为某个ViewResolver指定order值的话,默认值为Integer.MAX_ VALUE,对应的是最低优先级。

    如果为DispatcherServlet指定多个ViewResolver的话,不要给予InternalResour- ceViewResolver以及其他UrlBasedViewResolver子类过高的优先级,因为这些ViewResolver即使找不到相应的视图,也不会返回null以给我们轮询下一个ViewResolver的机会,这样,我们所指定的其他ViewResolver实际上就形同虚设。合理的处理方式是,给予ResourceBundleView- Resolver或者XmlViewResolver这种能够通过返回null以表明无法找到相应视图的ViewResolver较高的优先级,而只是将InternalResourceViewResolver(或者其他类似行为的ViewResolver)添加为最低优先级ViewResolver,以作为DispatcherServlet的后备查找对象。

  • 相关阅读:
    asp.net mvc controller调用js
    无刷新文件上传 利用iframe实现
    Git使用
    easyui扩展
    Highcharts 多个Y轴动态刷新数据
    Android之Handler
    asp.net mvc之TempData、ViewData、ViewBag
    android之滑屏的实现
    java多线程系类:JUC线程池:05之线程池原理(四)(转)
    java多线程系类:JUC线程池:04之线程池原理(三)(转)
  • 原文地址:https://www.cnblogs.com/chenying99/p/2427901.html
Copyright © 2020-2023  润新知