Velocity 模板语言学习入门(一)

0x00 前言

  Apache Velocity 是一个老牌 Java 模板语言。相对于另一个老牌模板语言 JSP 而言,Velocity 语法和结构上更加灵活和丰富,因此除了 JSP (JSP 是官方、正统正妻身份)之外的一个比较受欢迎的选择,大多数的 Web Framework 也因此将其作为内置支持模板语言之一,例如 SpringMVC。   所谓模板语言,就是用语言编写出一个模板文件(例如一个动态网页),然后提供具体的数据,经由模板语言引擎处理,最终输出所需要的内容。下面是模板语言通用理解图:

示意图

  到目前为止,Velocity 的最高版本是 1.7 (2.0 还未出来),所以下面的内容也是针对这个版本来说。

0x01 引入

  我们可以使用下面的 Maven 来引入 Velocity 包:

<dependency>
  <groupId>org.apache.velocity</groupId>
  <artifactId>velocity</artifactId>
  <version>1.7</version>
</dependency>
0x10 起航
  • 文件扩展名: vm

  • **文件存放路径,默认在 “.” **

    • 可以放到 classes 中,设置比较简单:
        VelocityEngine ve = new VelocityEngine();   // Velocity 模板引擎
        Properties props = new Properties();
        props.setProperty(VelocityEngine.RESOURCE_LOADER, "classpath");
        props.setProperty("classpath." + VelocityEngine.RESOURCE_LOADER + ".class",
                          ClasspathResourceLoader.class.getName());

        ve.init(props);
+ 如果要放到 WEB-INF 就比较麻烦,不过可以参考或使用:Velocity-tools。 它提供了 Servlet 和 Struts1 的实现,如果项目也刚好是这样的 Web 框架,直接用就可以。如果不是,也可以直接将 org.apache.velocity.tools.view.WebappResourceLoader 拷贝出来用。
  • Hello World
<!-- 存成文件 hello.vm -->
<html>
<body>
  #set( $foo = "Velocity" )
  Hello $foo World!
</body>
<html>
public class HelloTest {

    /**
     *
     * @param args
     */
    public static void main(String[] args) {

        //初始化并取得Velocity引擎
        VelocityEngine ve = new VelocityEngine();
        Properties props = new Properties();
        props.setProperty(VelocityEngine.RESOURCE_LOADER, "classpath");
        props.setProperty("classpath." + VelocityEngine.RESOURCE_LOADER + ".class",
                          ClasspathResourceLoader.class.getName());

        ve.init(props);

        //取得velocity的模版,HelloTest 放在根目录,如果放在包中:例如  my.test.HelloTest ,那么就要用 my/test/hello.vm
        Template t = ve.getTemplate("hello.vm");

        //取得velocity的上下文context
        VelocityContext context = new VelocityContext();

        //输出流
        StringWriter writer = new StringWriter();

        //转换输出
        t.merge(context, writer);

        System.out.println(writer.toString());

    }
}
0x10 语法规范
  • **规范一:使用 “##”、“#...#”、“#[[...]]#” 来做注释 **
      ## 我是行注释          <==== 这个不会输出任何东西
	  
      #*                    <==== 这个会输出一个空行
        我是块注释
      *#
	  
      #**                   <==== 这个也会输出一个空行
        我是文档注释
      **#
	  
	  #[[                  <==== 这个里面的内容会输出
	      本注释的内容不被 Velocity 引擎解释,但会原样输出  
	  ]]#
  • **规范二:使用 "$" + 标识符来定义变量,变量不需要指定类型 **

    • 标识符:符合 Java 变量定义规范即可。

    • 常用示例: $name,$templateName,$request 等等

     #set($name="David")
     #set($templateName = "header.vm")
     #set($width = 100)
  • 如果变量是对象,可以用“.”(点号)操作属性。对!是属性(即带 set/get 方法的成员变量)!
     项目名称:$project.name       ## 这里等价于 $project.getName()
     
     #set($template.path = "WEB-INF/")   ## 这里等价于 $template.setPath("WEB-INF")
     #set($issue.reportor.name = "David")
  • 兼容定义:${name}、${templateName}。随个人喜好使用。但在一些特定场合,例如可能会跟内容混淆在一起时,只能使用这种定义方式。
  width: ${width}px;  /* 如果用 $widthpx ,就乱了 */
  • 加“!”(叹号)为空显示。即当变量不存在,又或者是 null 时,强制显示为空白。
   $!name
   $!email  或者  $!{email}
  • **规范三:象 Java 那样调用对象的方法 **
       //java 方面,将对象放入 velocity 的上、下文对象中(VelocityContext)
      context.put("template", new Template());

      $template.show()      ##<-- 执行 template 对象的 show() 方法
  • 规范四:使用“[ ]”定义列表,使用“{ }”定义 Map,$list[index] 进行列表和 Map 访问

    • 使用“[]” 和 “{}” 定义列表或 Map
      ## 定义列表
      $set( #list=[1, 2, 3, 4])      ## 数字列表
      $set( #list=[1..10])           ## 使用范围来定义数字列表
      $set( #list=['red', 'blue', 'green'])  ## 字符串列表
      
      $set( #map={'name':'David', 'address':'address'})  ## map 即键值表
  • 列表元素访问:$list[index] 或者 $list.get(index)
      ## 要注意,Velocity 目前版本并不能支持数组的元素直接访问,要访问单独元素时,只能将数组转为列表,如 ArrayList
      $set( #list = [0..10] )   ## 定义一个列表
      #list[3]                  ## 相当于 list.get(3)
      
      $set( #strs = ['name', 'address'] ) ## 定义字符串列表
      #strs[1]      ## 列表元素
      
      $set( #map = {'name':'David', 'job':'normal'}) ## 定义一个 map
      #map['name']   ## 访问 name 键的值,相当于 map.get('name')
      #map.job       ## 也是访问键值的方法,相当于 map.get('job')
  • **规范五:#set 赋值语句,定义和给变量赋值 **
     ## 要对变量进行赋值,只能通过 #set 语句进行
     $set( $var = "value" )
     $var                      ## 这里输出是 “value” 值
     $var="Hello"              ## 这不是赋值,本行的结果是:value="Hello"
     
     $set( $bill = $price + 10)
     $set( $monkey.friend = "monica" )
     $set( $monkey.Say = ["Not", $my, "fault"])
     $set( $result = $query.criteria("address") )
  • **规范六:#if 条件语句,进行分支处理 **

    • 完整语句:#if(条件) ... #elseif(条件) ... #else ... #end
    #if($man)
      <strong>$man</Strong>
    #elseif($man.age > 40)
      老员工
    #elseif(!$man.married)
      未婚
    #else
      其他
    #end
  • 布尔值:true, false。除了 true 之外,如果变量的值不为 null,列表或map 不为空,也看作 true 。
    #if($man)                     ## 因为 $man 未定义,所以走 #else 这个分支
      <strong>$man</strong>
    #else
      未定义
    #end
    
    #set( $man="David" )          ## 已经给 $man 赋值,即使不是布尔值,但是不为 null,所以也为 true
    #if($man)
      <strong>$man</strong>       ## 本例将执行这个分支
    #else
      未定义
    #end
  • 逻辑表达式:&&、||、! 同 java 一样的操作符
    #if( $foo && $bar )
      这是 AND 操作
    #elseif( $head || $menu )
      这是 OR 操作
    #elseif( !$query )
      这是 not 操作
    #end
  • 关系表达式:==、>、<、!= 同 java 一样的操作符。要注意,在这里 == 是值比较,不是 java 那个引用比较
    #if( $color=="blue" )
      color: $color;
    #{elseif}( $width < 1000 )          ## #elseif 也可以写成 #{elseif}
      width: 900 px;
    #end
  • **规范七:#foreach 循环语句,列表、数组或 map 的循环遍历。注意:可以用来遍历数组。 **

    • 语句结构: #foreach ( $var in $list ) ... #end
    ## 列表遍历
    <table>  ## 列出所有客户的名称
    #foreach( $customer in $customerList )
       <tr><td>$customer.name</td></tr>
    #end
    </table>
    
    ## Map 表的遍历
    <table>   ## 假设 $customerList 是一个 <name, customer> 键值表
    #foreach( $name in $customerList.keyset() )  ## 遍历键
       <tr><td>$name</td></tr>
    #end
    </table>
  • 计数($foreach.count)、索引值($foreach.index)和中止遍历(#break)
     ## $foreach 是循环内部自动生成的变量,记录循环的信息
     $foreach.count     ## 循环计数,从 1 开始每循环一次加1
     $foreach.index     ## 遍历列表的索引值,从0开始
     $foreach.hasNext   ## 是否还有下一个(遍历),可以用来判断是否到达最后
     $foreach.first     ## 第一个元素
     $foreach.last      ## 最后一个元素
     $foreach.parent    ## 外层循环
     
     <table>
     #foreach( $customer in $customerList )
         <tr><td>$foreach.count</td><td>$customer.name</td></tr>
     #end
     </table>
     
     ## 显示前五个客户
     <table>
     #foreach( $customer in $customerList )
         #if( $foreach.count > 5 )
            #break
         #end
         <tr><td>$foreach.count</td><td>$customer.name</td></tr>
     #end
     </table>
  • 可以在 velocity.properties 中配置全局的最大循环次数,作为一种防护手段
     directive.foreach.maxloops = -1   ## -1 是默认值,表示不限制
  • **规范八:#include、#parse 引入外部资源和模板文件 **

    • #include(file1, file2,....) 导入外部资源,但不进行解释
    #include( "license.txt", $footer )
  • #parse( file ) 导入 Velocity 模板文件并解释,结果原地输出。下层模板能够使用上层模板的变量
      ## parent.vm
	  #set( $list = [1..10])
	  #parse( "child.vm" )
	  
	  ## child.vm
	  parent's $list    ## 使用父模板的变量
  • **规范九:#macro 、#define 自定义可重用模块 **

    • #macro 宏定义,定义可带参数的模板内函数:#macro( name [$param1 $param2 ...]) ... #end。参数可以是零或多个。
      ## 可以将宏定义看作是模板内函数,由名称和参数组成
	  #macro( log $color $message )      ## 定义了一个名称为 log 的宏,并带两个参数: $color 和 $message
	    <p style="color:$color">log: $message</p>
      #end
	  
	  ## 使用时,可以当成普通函数一样用
	  #log("blue" "log 输出...")           ## 在使用时,要象函数那样加上"()" 小括号
	  
	  ## 没参数的宏
	  #macro( show )
	    Hello World!
	  #end
	  
	  ##使用,要加上小括号
	  #show()
  • 宏在使用时,传入的数据个数,不需要与定义中的参数个数一样。
      ## 可以将宏定义看作是模板内函数,由名称和参数组成
	  #macro( log $color $message )      ## 定义了一个名称为 log 的宏,并带两个参数: $color 和 $message
	    <p style="color:$color">log: $message</p>
      #end
	  
	  ## 可以省略后面的参数
	  #log('blue')          ## 只输入一个参数,输出结果为:<p style="color: blue">log: $message</p>   其中 $message 原样输出,不过如果在宏外面定义了 $message 的值,在这里也可以输出
	  
	  ## 也可以传入更多的数据,当然,会被忽略
	  #log('blue' '参数2' '参数3')         ## 多出来的参数由于没有使用而被忽略
  • #define 定义可重用块,是 #macro 不带参数的简化版
     #define( $block )     ## 定义块引用,引用名称为 $block
	    Hello World!
     #end
	 
	 $block          ## 进行引用,要留意的是,不需要添加 “()”,这是与 macro 唯一不同的地方
  • 建立一个全局的、独立的可重用库。我们可以将常用的宏聚集起来形成可重用库。
      ## 下面是一些相关的配置
	  velocimacro.library——用逗号分隔的一组文件名,是Velocity宏模板库。默认值是VM_global_library.vm
	  velocimacro.permissions.allow.inline——宏是否可以在一个普通模板中定义。默认值是false,表示可以。
	  velocimacro.permissions.allow.inline.to.replace.global——是否允许模板中的宏覆盖library中的宏。默认值是false,表示不可以覆盖。
	  velocimacro.permissions.allow.inline.local.scope——一个在普通模板中定义的宏,是否允许其他模板使用。默认是false。
	  velocimacro.context.localscope——在一个宏里通过#set()修改了context,此修改是否仅仅对这个宏自身,而不是永久性修改了context。默认值是false。
	  velocimacro.library.autoreload——Velocity宏模板库修改之后,是否自动重新加载。默认值是false。debug时可以设置为true,发布时设置为false。
  • **规范十:#evaluate 动态执行字符串,#stop 中止并退出模板解释 **

    • #evaluate 可以执行内嵌 Velocity 指令的字符串。我们可以在有需要时,动态生成一些指令,然后由本方法执行。
     ## 根据需要生成命令,暂时找不到好例子,就用官方文档的例子
     #set($source1 = "abc")
     #set($select = "1")
     #set($dynamicsource = "$source$select")
     
	 ## $dynamicsource is now the string '$source1'
     #evaluate($dynamicsource)
  • #stop 中止并退出模板解释。在调试或出现严重错误,需要中止时有用。
     #stop    ## 中止
	 
	 #stop( '$order was not in context' )   ## 可以加一个中止说明,这个说明将会写入到日志文件中去
0x11 还未结束

  任何一项技术(不紧紧是 Velocity)在用时,都是三大件:环境,使用和深入。环境当然包含了如何加入到项目中,如何加载和配置配置项;灵活使用满足项目需求;深入掌握技术的精髓,助力项目在满足需求情况下,减少开发成本,提高稳定性和可维护性。   本文只是说明基础的规范与使用,如何灵活运用和深入掌握,还得要多用、多想、多看。

转载于:https://my.oschina.net/delphixp/blog/501055

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值