文档章节

Velocity模板引擎

坚持不懈丶
 坚持不懈丶
发布于 2015/07/24 14:17
字数 1767
阅读 174
收藏 2

Velocity 是一个基于 Java 的模板引擎,它允许用户使用简单的模板语言来引用由 Java 代码定义的对象。当 Velocity 应用于 Web 开发时,界面设计人员可以和 Java 程序开发人员同步开发一个遵循 MVC 架构的 Web 站点。也就是说,页面设计人员可以只关注页面的显示效果,而 Java 程序开发人员关注后台业务逻辑的编码。 Velocity 将 Java 代码从 Web 页面中分离出来,这样为 Web 站点的长期维护提供了便利,同时也为我们在 JSP 和 PHP 之外又提供了一种可选的方案。

Velocity 的能力不仅仅用于 Web 开发领域,它也可以被当作一个独立工具来产生源代码和报告(例如,可以产生 SQL 和 PostScript、XML 等),或者作为其他系统的集成组件使用。

下面是一个简单的用 Velocity 编写的网页代码:

<html> 
 <head> 
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
  <meta http-equiv="Content-Language" content="zh-CN"/> 
  <title>$page_title</title> 
  <link rel="stylesheet" href="osc-global.css" type="text/css" /> 
 </head> 
 <body> 
 <div id="OSC_top"> 
         #parse("header.vm") 
 </div> 
 <div id="OSC_content"> 
 <table> 
         #foreach($idx in [1..20]) 
         <tr> 
                 <td>$velocityCount</td> 
                 <td>Now is $date.now()</td> 
         </tr> 
         #end 
 </table> 
 </div> 
 <div id="OSC_bottom"> 
         #include("bottom.vm") 
 </div> 
 </body> 
 </html>

在 Velocity 模板语言的语法中,以美元符 $ 开头的为变量的声明或者引用,而以井号 # 开头的语句则为 Velocity 的指令(Directive)。其中 Velocity 的指令又分为内置指令、自定义宏和自定义指令。 Velocity 包含下列这些内置指令

另外也可以通过宏来扩展指令,例如,请看下面这个宏的定义

#macro(invoke $__p_page) 
    #if($__p_page.startsWith("/")) 
        #parse($__p_page) 
    #else 
        #set($__uri = $resource.this_vm()) 
        #set($__path = $__uri.substring(0, $__uri.lastIndexOf("/"))) 
        #parse("$__path/$__p_page") 
    #end 
 #end

上面这个名为 invoke 的宏的作用是以相对路径的方式嵌入某个动态的页面。使用的方法是 #invoke( “ hello.vm ” )。尽管 Velocity 的自定义宏可以用来扩展指令,但是宏有一个不足的地方,它更适合用来执行一些比较通用的代码嵌入和简单的功能处理。

由此引出来的问题是,我如何来编写一个类似于 #foreach 的指令呢,并具有逻辑判断的功能?

Velocity 的指令类型简介

在 Velocity 的指令定义上,有两种指令类型分别是行指令和块指令。行指令例如 #set($name= ” Winter Lau ” ) 赋值指令,只有一行,中间没有任何的代码;而块指令例如循环指令 #foreach($idx in [1..20]) $idx #end,块指令需要用 #end 来结束。在 Velocity 自带的指令中块指令包括有:#if #elseif #foreach #define 和 #macro 这几个指令,除此之外都是行指令。

编写自定义的 Velocity 指令

Velocity 允许您对指令系统进行扩展,在 Velocity 引擎初始化的时候会加载系统内置指令和用户的自定义指令。系统的内置指令已经在 Velocity 的 Jar 包中的 directive.properties 文件中定义,不建议直接修改该文件。而自定义的指令要求用户在 velocity.properties 文件中定义的,例如:userdirective=net.oschina.toolbox.CacheDirective。如果是多个自定义指令则使用 逗号隔开。

所有的自定义指令要求扩展 org.apache.velocity.runtime.directive.Directive 这个类。为了更加形象直观的表现 Velocity 自定义指令的优点,接下来我们将以一个实际的应用场景进行讲解。

在该应用场景中,所有的页面请求直接指向 vm 文件,中间没经过任何的控制器。数据是通过 Velocity 的 toolbox 直接读取并显示在页面上。如果数据是来自数据库的,而且访问量非常大的时候,我们就需要对这些数据进行缓存以便快速响应用户请求和降低系统负载。一种方法 是直接在 toolbox 的读取数据的方法中进行数据的缓存;另外一种就是我们接下来要介绍的,通过编写自定义的缓存指令来缓存页面上的某个 HTML 片段。

首先我们定义一个这样的块指令:#cache( “ CacheRegion ” , ” Key ” ) ,其中第一个参数为缓存区域、第二个参数为对应缓存数据的键值。该指令自动将包含在指令内部的脚本执行后的结构缓存起来,当第一次请求时检查缓存中是否存 在此 HTML 片段数据,如果存在就直接输出到页面,否则执行块指令中的脚本,执行后的结果输出到页面同时保存到缓存中以便下次使用。使用方法如下所示:

#cache("News","home") 
  ## 读取数据库中最新新闻并显示
  <ul> 
  #foreach($news in $NewsTool.ListTopNews(10)) 
         <li> 
          <span class='date'> 
 $date.format("yyyy-MM-dd",${news.pub_time}) 
 </span> 
          <span class='title'>${news.title}</span> 
         </li> 
  #end 
  </ul> 
 #end

其中 $NewsTool.ListTopNews(10) 是用来从数据库中读取最新发布的 10 条新闻信息。

接下来我们来看 #cache 这个指令对应的源码:

/**
 * Velocity模板上用于控制缓存的指令
 */
public class CacheDirective extends Directive {

    final static Hashtable<String,String> body_tpls = new Hashtable<String, String>();
        
    @Override
    public String getName() { return "cache"; } //指定指令的名称

    @Override
    public int getType() { return BLOCK; } //指定指令类型为块指令

    /* (non-Javadoc)
    * @see org.apache.velocity.runtime.directive.Directive#render()
    */
    @Override
    public boolean render(InternalContextAdapter context, Writer writer, Node node)
        throws IOException, ResourceNotFoundException, ParseErrorException,
        MethodInvocationException 
    {
        //获得缓存信息
        SimpleNode sn_region = (SimpleNode) node.jjtGetChild(0);
        String region = (String)sn_region.value(context);
        SimpleNode sn_key = (SimpleNode) node.jjtGetChild(1);
        Serializable key = (Serializable)sn_key.value(context);
     
        Node body = node.jjtGetChild(2);
        //检查内容是否有变化
        String tpl_key = key+"@"+region;
        String body_tpl = body.literal();
        String old_body_tpl = body_tpls.get(tpl_key);
        String cache_html = CacheHelper.get(String.class, region, key);
        if(cache_html == null || !StringUtils.equals(body_tpl, old_body_tpl)){
            StringWriter sw = new StringWriter();
            body.render(context, sw);
            cache_html = sw.toString();
            CacheHelper.set(region, key, cache_html);
            body_tpls.put(tpl_key, body_tpl);
        }
        writer.write(cache_html);
        return true;
    }
}

Directive 是所有指令的基类,Directive 是一个抽象类,它有三个方法必须实现的,分别是:

•getName:返回指令的名称

•getType:返回指令的类型,行指令:LINE、块指令:BLOCK

•render:指令执行的入口

其中 render 方法的最后一个参数 node 表示为该指定对应在 Velocity 模板中的节点对象,通过调用 node 的 jjtGetChild 方法可以获取到传递给该指令的参数以及包含在该指令的脚本内容。

上面的代码中,首先获取传递给指令的参数,也就是缓存的区域名和对应缓存数据的键值。 接着判断距上次数据被缓存时,指令所包含的脚本代码是否有更改(以便页面开发人员修改了 vm 脚本时自动刷新缓存数据),然后判断缓存中是否已有数据。当缓存中无数据或者页面代码被修改时,重新执行块指令中的脚本并将执行的结果置入缓存,否则直接 将缓存中的数据输出到页面。

上述例子中,传递给 #cache 指令的参数也可以是某个变量,例如

#set($region = "news") 
 #set($key = "home") 
 #cache("CACHE_$region",$key)

如此,便以很小的代码侵入,来实现页面的缓存。

© 著作权归作者所有

共有 人打赏支持
坚持不懈丶
粉丝 2
博文 16
码字总数 13904
作品 0
杭州
程序员
私信 提问
TinyTemplate模板引擎火热出炉,正式开源了~~~

涉水模板引擎领域,纯属不小心。 在此对以下人员表示强烈感谢与致敬: @sub jetbrick作者 @sept @webit webit作者 @罗格林 rythm作者 @闲.大赋 beetl作者 以及许许多多虽然没有列出来,但是在...

悠悠然然
2014/06/18
0
29
Tiny模板引擎--TinyTemplate

Tiny模板引擎 是一个基于Java技术构建的模板引擎,它具有体量小、性能高和扩展易的特点。 适合于所有通过文本模板生成文本类型内容的场景,如:XML、源文件、HTML等等,可以说,它的出现就是...

悠悠然然
2014/06/18
2.2K
1
Apache Velocity实现模板化

1.参考文献 http://www.cnblogs.com/zhuboxingzbx/articles/1225103.html http://www.cnblogs.com/amboyna/archive/2008/06/19/1225105.html 2.概述 2.1.Velocity模板引擎 模板引擎的设计思想......

嗯哼9925
2017/12/20
0
0
Velocity找不到模板

最近工作中需要用到模板解析的功能,找到了velocity,按照例子做了一遍,没有问题, 于是乎,直接用于工作环境,发现总是报Resource not found 的异常, 检查模板路径,并没有写错什么,但是...

穿越星辰
2010/05/13
295
0
Java模板引擎 HTTL

HTTL(Hyper-Text Template Language)是一个高性能的开源JAVA模板引擎,适用于动态HTML页面输出,可替代JSP页面,指令和Velocity相似。 简洁友好的模板语法 语法尽可能符合HTML和JAVA开发者...

kevin728971010
2016/11/07
59
0

没有更多内容

加载失败,请刷新页面

加载更多

C++ vector和list的区别

1.vector数据结构 vector和数组类似,拥有一段连续的内存空间,并且起始地址不变。 因此能高效的进行随机存取,时间复杂度为o(1); 但因为内存空间是连续的,所以在进行插入和删除操作时,会造...

shzwork
今天
3
0
Spring之invokeBeanFactoryPostProcessors详解

Spring的refresh的invokeBeanFactoryPostProcessors,就是调用所有注册的、原始的BeanFactoryPostProcessor。 相关源码 public static void invokeBeanFactoryPostProcessors(Configu......

cregu
昨天
4
0
ibmcom/db2express-c_docker官方使用文档

(DEPRECIATED) Please check DB2 Developer-C Edition for the replacement. What is IBM DB2 Express-C ? ``IBM DB2 Express-C``` is the no-charge community edition of DB2 server, a si......

BG2KNT
昨天
3
0
Ubuntu 18.04.2 LTS nvidia-docker2 : 依赖: docker-ce (= 5:18.09.0~3-0~ubuntu-bionic)

平台:Ubuntu 18.04.2 LTS nvidia-docker2 版本:2.0.3 错误描述:在安装nvidia-docker2的时候报dpkg依赖错误 nvidia-docker2 : 依赖: docker-ce (= 5:18.09.0~3-0~ubuntu-bionic) 先看一下依......

Pulsar-V
昨天
4
0
学习笔记1-goland结构体(struct)

写在前面:若有侵权,请发邮件by.su@qq.com告知。 转载者告知:如果本文被转载,但凡涉及到侵权相关事宜,转载者需负责。请知悉! 本文永久更新地址:https://my.oschina.net/bysu/blog/3036...

不最醉不龟归
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部