【Jsp精品源码栏目提醒】:网学会员鉴于大家对Jsp精品源码十分关注,论文会员在此为大家搜集整理了“【精品】学习笔记 7.5 使用其他视图技术 - 其它资料”一文,供大家参考学习
7.5 使用其他视图技术 Spring 的 MVC 框架被设计为控制器和具体视图技术的完全解耦。
控 制器只需要返回视图的逻辑名称,至于如何解析这个名称,由 ViewRes olver 负责,因此,从一种视图技术(例如,
JSP)转到另一种视图技 术时,控制器无需更改,只要设定合适的 ViewResolver 即可。
下面要介绍的是除了
JSP 之外的其他视图技术。
Spring 提供了对 Velocity、Freemaker 和 XSLT 的完善支持。
7.5.1 Velocity Velocity 是一种模板技术,能够通过模板生成任何文本内容,因此也非常适合作为视图。
Velocity 提供了一种非常简单的模板语言 VTL,很容易同时被 Java 开发人员和网页设计人员轻松理解,和
JSP 相比,Velocity 在以下几点更具优势。
(1)Velocity 不提供 Java 代码支持,这意味着它只能作为视图,无法嵌入任何逻辑代码。
Velocity 的这种设计就是为了强制分离
Java 逻辑和 Web 页面,使 Web 应用程序更容易维护,而
JSP 允许嵌入任意的 Java 代码,很容易造成 Java 代码的滥用。
(2)Velocity 不需要特定的标签,它使用简单的var_name来标记变量,这使得页面设计更加容易,因为
JSP 的标签代码通常在可视化的 HTML 编辑器中无法正常显示。
Velocity 页面还能使用任意扩展名,包括.html,因此,可以直接在浏览器中预览页面的效果。
(3)通常,Velocity 还提供了比
JSP 更快的渲染速度。
要在 Spring MVC 框架中应用 Velocity,首先需要配置 Velocity 引擎。
在 Spring 中使用 Velocity 比单独使用 Velocity 更加容易,也更加方便,我们甚至根本不需要与 Velocity 的 API 打交道,所有 Velocity 相关组件均是在 XML 配置文件中定义的。
我们将 Spring MVC 项目复制一份,命名为 Spring_Velocity,结构如图 7-41 所示。
Velocity 需要 velocity.jar 和 commons-collections.jar 这两个 jar 包,将其与 Spring 的相关 jar 包一同放入/web/WEB-INF/lib 目录下。
由于 Velocity 不需要 taglib,修改 web.xml,将 taglib 的声明删除。
ltxml versionquot1.0quot encodingquotUTF-8quotgt ltDOCTYPE web-app PUBLIC quot-//Sun Microsystems Inc.//DTD Web Application 2.3//ENquot quothttp://java.sun.com/dtd/web-app_2_3.dtdquotgt ltweb-appgt ltservletgt ltservlet-namegtdispatcherlt/servlet-namegt ltservlet-classgtorg.springframework.web.servlet.DispatcherServletlt/ servlet-classgt ltload-on-startupgt0lt/load-on-startupgt lt/servletgt ltservlet-mappinggt ltservlet-namegtdispatcherlt/servlet-namegt lturl-patterngt.htmllt/url-patterngt lt/servlet-mappinggt lt/web-appgt 然后修改 dispatcher-servlet.xml,添加 velocityConfigurer 配置 Velocity 引擎,并设置 viewResolver 为 VelocityViewResolver。
ltxml versionquot1.0quot encodingquotUTF-8quotgt ltbeans xmlnsquothttp://www.springframework.org/schema/beansquot xmlns:xsiquothttp://www.w3.org/2001/XMLSchema-instancequot xsi:schemaLocationquothttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdquotgt ltbean namequot/test.htmlquot classquotexample.chapter7.TestControllerquot /gt lt-- 使用 Velocity 视图解析器 --gt ltbean idquotviewResolverquot classquotorg.springframework.
web.servlet.view. velocity.VelocityViewResolverquotgt ltproperty namequotcontentTypequot valuequottext/htmlcharsetUTF-8quot /gt ltproperty namequotprefixquot valuequot/quot /gt ltproperty namequotsuffixquot valuequot.htmlquot /gt lt/beangt lt-- 配置 Velocity --gt ltbean idquotvelocityConfigquot classquotorg.springframework.web.servlet.view. velocity.VelocityConfigurerquotgt lt-- 配置文件位置 --gt ltproperty namequotconfigLocationquot valuequot/WEB-INF/velocity.propertiesquot /gt lt-- 视图资源位置 --gt ltproperty namequotresourceLoaderPathquot valuequot/quot /gt lt/beangt lt/beansgt Velocity 还需要一个配置文件,用于设置 Velocity 引擎。
配置文件的每个选项都可以注入到 Spring 的 XML 配置文件中,但是,由于 Velocity 的配置选项较多,放在单独的 velocity.properties 中比较合适。
典型的配置如下,读者只需要注意几个重要的配置选项。
runtime.log.logsystem.class org.apache.velocity.runtime.log.SimpleLog4JLogSystem runtime.log example.chapter7 runtime.log.error.stacktrace true runtime.log.warn.stacktrace true runtime.log.info.stacktrace false runtime.log.invalid.reference true 设置输入/输出的编码: input.encoding UTF-8 output.encoding UTF-8 directive.foreach.counter.name velocityCount 设置 foreach 循环的 index 初始值: directive.foreach.counter.initial.value 1 directive.include.output.errormsg.start lt-- include error : directive.include.output.errormsg.end see error log --gt directive.parse.max.depth 3 设置输入模板来自文件: resource.loader file file.resource.loader.description Velocity-File-Resource-Loader 设置模板的加载类: file.resource.loader.class org.apache.velocity.runtime.resource.loader. FileResourceLoader 设置是否使用 cache在开发期可禁用 cache 以便随时编辑页面: file.resource.loader.cache false 设置检测文件改动的时间间隔在运行期可设置较大的数如 36001 小时: file.resource.loader.modificationCheckInterval 1 设置 macro 文件位置: velocimacro.library /macro.txt velocimacro.library.autoreload true VTL Velocity 使用一种 VTL 语言来渲染视图, 有非常简单的语法,它通过var_name来输出变量,支持循环, 将 条件判断和赋值。
test.
jsp 重命名为 test.html,修改内容如下。
lthtmlgt ltheadgt ltmeta http-equivquotContent-Typequot contentquottext/html charsetutf-8quotgt lttitlegtSpring_Velocitylt/titlegt lt/headgt ltbodygt lth3gtHello name it is timelt/h3gt lt/bodygt lt/htmlgt 虽然 Velocity 一般使用.vm 作为页面的扩展名,不过,Velocity 视图是纯 HTML 页面,因此我们使用.html 作为扩展名,这样在可视化 HTML 编辑器中编辑时非常直观,并且可以在不启动服务器的情况下就能预览页面效果,大大简化了页面的
设计和实现,如图 7-42 所示。
启动 Resin 服务器,输入 http://localhost/test.html,可以看到由 Velocity 渲染的页面,如图 7-43 所示。
图 7-42 图 7-43 Velocity 还提供了 Macro(宏)的功能,能将反复引用的一组复杂的 HTML/Velocity 代码通过一个 Macro 定义并引用,极大地简化页面的设计。
Macro 相当于
JSP 的自定义标签,但
JSP 的自定义标签编写极其复杂,而用户编写的 Macro仍是 HTML/Velocity 代码,因此相对容易得多。
7.5.2 Freemarker 和 Freemaker 是取代
JSP 的又一种视图技术, Velocity 非常类似,但是它比Velocity 多了一个格式化的功能,因此使用上较 Velocity 方便一点,但语法也稍微复杂一些。
将 Velocity 替换为 Freemarker 只需要改动一些配置文件,同样,在 Spring 中使用 Freemarker 也非常方便,根本无须与 Freemarker 的 API 打交道。
我们将 Spring_Velocity 工程复制一份,命名为 Spring_Freemarker,结构如图 7-44 所示。
图 7-44 修改 dispatcher-servlet.
xml,将 velocityConfig 删除,修改 viewResolver 为 FreeMarker ViewResolver,并添加一个 freemarkerConfig。
lt-- 使用 Freemarker 视图解析器 --gt ltbean idquotviewResolverquot classquotorg.springframework.web.servlet.view. freemarker.FreeMarkerViewResolverquotgt ltproperty namequotcontentTypequot valuequottext/htmlcharsetUTF-8quot /gt ltproperty namequotprefixquot valuequot/quot /gt ltproperty namequotsuffixquot valuequot.htmlquot /gt lt/beangt lt-- 配置 Freemarker --gt ltbean idquotfreemarkerConfigquot classquotorg.springframework.web.servlet.view. freemarker.FreeMarkerConfigurerquotgt lt-- 视图资源位置 --gt ltproperty namequottemplateLoaderPathquot valuequot/quot /gt ltproperty namequotdefaultEncodingquot valuequotUTF-8quot /gt lt/beangt 模板 test.html 可以稍做修改,加入 Freemarker 内置的格式化功能来定制 Date 类型的输出格式。
lthtmlgt ltheadgt ltmeta http-equivquotContent-Typequot contentquottext/html charsetutf-8quotgt lttitlegtSpring_Freemarkerlt/titlegt lt/headgt ltbodygt lth3gtHello name it is timestringquotyyyy-MM-dd HH:mm:ssquotlt/h3gt lt/bodygt lt/htmlgt 添加 freemarker.jar 到 web/WEB-INF/lib 目录后,启动 Resin,可以看到由Freemarker 渲染的页面,如图 7-45 所示。
图 7-457.5.3 XSLT XSLT(XSL 是 eXtensible Stylesheet Language 的缩写,而 XSLT 代表 XSLTransformations)技术是为了将 XML 格式的
文档转化为任意格式的文本
文档,当然,主要用途之一就是将 XML 转化为 HTML。
使用 XMLXSLT 能实现最严格的数据和显示分离:XML 仅包含数据,而最终的渲染页面则交给 XSLT 来完成,如图 7-46 所示。
图 7-46 XSLT 可以实现 XML 到 HTML 的转化,转化过程既可以在服务器端完成,也可以在浏览器端完成。
如果在服务器端转化,则客户端根本不知道服务器端使用的具体技术,不过,由于 XSLT 的转化非常耗费 CPU 资源,因此,在访问量大的情况下,要考虑服务器是否能够负载,相比之下,在浏览器端转化就不需要耗费太多的服务器资源,服务器只需要向浏览器传送 XML 和 XSLT 文件,由浏览器自己完成 XML 到 HTML 的转化,这种方式减轻了服务器的负担,不过,客户端就得知了服务器端采用的技术,并且早期的浏览器还不支持 XML 的转化,但是现在绝大多数浏览器都没有问题。
在浏览器端转化的另一个问题是搜索引擎无法正确地抓取页面,因为
搜索引擎一般只抓取 HTML 格式的页面,对于仅包含纯数据的 XML,搜索引擎一般无法分析其内容。
在 Spring 中使用 XSLT 和其他视图技术类似,我们在 Eclipse 中新建一个 Spring_Xslt 工程,结构如图 7-47 所示。
图 7-47 其中,TestController 不变,test.html 更改为 test.xslt,负责将 XML 转化为
HTML。
这里的一个
问题是,Spring 的 Controller 返回的 Model 是 Map 类型,必须将其首先转化为 XML 才能用 XSLT 完成到 HTML 的转化。
遗憾的是,从 Map类型到 XML 没有直接的转化过程,由于 Map 中包含的数据类型也各不相同,因此,还必须手动编写 TestXsltView 类,完成 Map 到 XML 的转化,该类必须从 AbstractXsltView 派生,并且复写 createXsltSource方法。
public class TestXsltView extends AbstractXsltView protected Source createXsltSourceMap model String rootName HttpServletRequest request HttpServletResponse response throws Exception Document document DocumentBuilderFactory.newInstance.newDocument Builder.newDocument Element root document.createElementrootName // 添加ltnamegt: String name Stringmodel.getquotnamequot Element nameNode document.createElementquotnamequot nameNode.appendChilddocument.createTextNodename root.appendChildnameNode // 添加lttimegt: Date time Datemodel.getquottimequot Element timeNode document.createElementquottimequot timeNode.appendChilddocument.createTextNodetime.toString root.appendChildtimeNode return new DOMSourceroot 为了使用 XSLT,在 dispatcher-servlet.xml 中配置使用 ResourceBundleViewResolver 作为 ViewResolver。
ltxml versionquot1.0quot encodingquotUTF-8quotgt ltbeans xmlnsquothttp://www.springframework.org/schema/beansquot xmlns:xsiquothttp://www.w3.org/2001/XMLSchema-instancequot xsi:schemaLocationquothttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdquotgt ltbean namequot/test.htmlquot classquotexample.chapter7.TestControllerquot /gt ltbean idquotviewResolverquot classquotorg.springframework.web.servlet.view. ResourceBundleViewResolverquotgt ltproperty namequotbasenamequot valuequotviewsquot /gt lt/beangt lt/beansgt 定义了 Map 到 XML 的转化后,还需要一个 views.properties 配置文件来告诉 Spring 该 TestXsltView 生成的 XML 应该用哪个 XSLT 来完成到 HTML 的转化,该文件必须放到 src 目录下。
test.classexample.chapter7.TestXsltView test.stylesheetLocation/test.xslt test.rootDocRoot 我们先把 test.stylesheetLocation 注释掉,这样 Spring 就可以直接将 XML 返回,我们首先检查生成的 XML 是否正确。
启动 Resin,输出如图 7-48 所示。
图 7-48 现在,我们编写 test.xslt 来转化生成的 XML 为 HTML,放到 web 目录之下。
ltxml versionquot1.0quot encodingquotutf-8quotgt ltxsl:stylesheet versionquot1.0quot xmlns:xslquothttp://www.w3.org/1999/XSL/Transformquotgt ltxsl:output methodquothtmlquot /gt ltxsl:template matchquot/quotgt lthtmlgtltheadgt lttitlegtSpring_Xsltlt/titlegt lt/headgt ltbodygt lth3gtHello ltxsl:value-of selectquotDocRoot/namequot /gt it is ltxsl:value-of selectquotDocRoot/timequot /gtlt/h3gt lt/bodygtlt/htmlgt lt/xsl:templategt lt/xsl:stylesheetgt 将 views.properties 的注释去掉,然后重新启动 Resin,可以看到由 XSLT转化而成的 HTML,如图 7-49 所示。
图 7-49 一般来说,如果使用 XSLT,由于视图部分总是要先设计好 HTML 页面,然后才能编写 XSLT 来转化 XML 到指定的 HTML 格式。
如果 HTML 页面结构发生了较大的改动,则整个 XSLT 都必须全部重写,其维护成本是非常高的。
目前,在还没有任何可视化编辑器辅助设计的情况下,不推荐使用 XSLT。
如果应用
程序本身已经有了 XML 数据,则可以考虑使用 XSLT 来完成 HTML 转化。
7.5.4 混合使用多种视图技术 如果需要混合使用多种视图技术,就需要设置合适的 ViewResolver,并且让其有能力解析不同的视图名称。
以 Web 形式启动的 Spring 应用程序会自动查找所有具有 ViewResolver 接口的 Bean,将它们作为一个视图解析链来使用。
默认地,声明在前的 ViewResolver 具有优先解析视图的权力,也可以用 order 属性来定义 ViewResolver 的解析顺序。
Spring 会按照 ViewResolver 的解析链来让每个 ViewResolver 试图解析并返回一个 View,如果某个 ViewResolver 返回了 View,则解析过程结束,然后调用View 对象的 render方法开始真正的渲染任务。
例如,InternalResourceViewResolver 会返回 JstlView 视图,VelocityViewResolver 会返回 VelocityView,FreeMarkerViewResolver 会返回 FreeMarkerView,不同的 View 会有不同的渲染方式。
不过,某些情况下,一个 ViewResolver 可能无法根据视图的逻辑名检测到一个 View 是否存在。
例如,InternalResourceViewResolver 只能调用 RequestDispatcher 来检测
JSP 文件是否存在,不幸的是,该方法只能调用一次,这可能会使后续的 ViewResolver 的解析出现问题。
另一个不太令我们满意的原因是使用 ViewResolver 链效率较低,它是通过循环来实现的,可以在 DispatcherServlet 中看到
源代码。
for Iterator it this.viewResolvers.iterator it.hasNext ViewResolver viewResolver ViewResolver it.next View view viewResolver.resolveViewNameviewName locale if view null return view 我们最好不要让 ViewResolver 链来解析视图,因为这样每个 ViewResolver只能依次“猜测”View 是否存在,如果能根据视图的扩展名来决定由哪个 ViewResolver 来解析,则只需要一步就可完成视图的解析。
一个可行的方法是实现一个自定义的 MixedViewResolver,并直接和 Spring MVC 框架关联,然后 MixedViewResolver 根据视图的扩展名来决定将解析委托给哪个具体的 ViewResolver。
例如,对于扩展名为.
jsp 的视图,就用 InternalResourceViewResolver 解析;对于扩展名为.vm 的视图,就用 VelocityViewResolver 解析;对于扩展名为.ftl 的视图,就用 FreeMarkerViewResolver 来解析。
这样,就实现了混合使用多种视图技术。
MixedViewResolver 仅实现 ViewResolver 接口,其实现的关键是将视图扩展名和具体的 ViewResolver 实例关联起来,从而实现根据视图扩展名来“转发”解析请求。
public class MixedViewResolver implements ViewResolver private MapltString ViewResolvergt resolvers public void setResolversMapltString ViewResolvergt resolvers this.resolvers resolvers public View resolveViewNameString viewName Locale locale throws Exception int n viewName.lastIndexOf. ifn-1 throw new NoSuchViewResolverException // 获得扩展名: String suffix viewName.substringn1 // 取出对应的 ViewResolver: ViewResolver resolver resolvers.getsuffix ifresolvernull return resolver.resolveViewNameviewName locale // 没有找到对应的 ViewResolver 就抛异常: throw new NoSuchViewResolverExceptionquotNo ViewResolver for quot suffix 由于需要根据视图的扩展名来决定到底使用何种 ViewResolver,所以就.