天天看点

FreeMarker标签与使用

模板技术在现代的软件开发中有着重要的地位,而目前最流行的两种模板技术恐怕要算freemarker和velocity了,webwork2.2对两者都有不错的支持,也就是说在webwork2中你可以随意选择使用freemarker或velocity作为view,模板技术作为view的好处是很多,尤其和jsp比较起来优点更大,众所周知jsp需要在第一次被执行的时候编译成servlet,那么这个过程是很慢的,当然很多应用服务器都提供预编译的功能,但是在开发的时候仍然给我们程序员带来了很多痛苦,每次修改都要多几秒钟,那在一天的开发中就有很多时间浪费在jsp的编译上了。用webwork

in action的作者的话来说:“每次修改之后重新运行都要等等几秒是令人失望的,而频繁地修改jsp更是会令你的失望情绪变本加厉“。我们把模板技术引入到view中去可以带来更好的开发效率,而且模板的速度要比jsp快(虽然编译过后的jsp在速度上已经满足我的需求了,呵呵)。 当然模板技术可以用在很多领域,可不只在view那里。我们可以通过模板技术来生成xml,生成jsp,生成java文件等等,说到这里,大家通常会使用模板技术用在公司的框架里,这样就可以很快速的生成添删改查的代码,需要的只是模板,其他比如还有邮件模板等等。 

    以上是模板的作用,那么现在开源的模板技术有好几种,多了之后就有一个选择的问题了,如何选择一个满足自己需要的模板的呢,写了一个例子,我使用了几种设计模式来完成了这个例子,这个例子中,同时使用了freemarker和velocity,这样同学们可以通过代码很直观的比较两种模板技术,通过这个例子,我认识到freemarker在功能上要比velocity强大 

1。在view层的时候,它提供了format日期和数字的功能,我想大家都有在页面上format日期或数字的经验,用jsp的同学可能对jstl的fmt标签很有感情,使用了freemarker之后也可以使用freemarker提供的功能来formmat日期和数据,这个功能我想是很贴心的

2。通过我的使用我发现freemaker的eclipseplugin要比velocity的eclipseplugin好,好在很多地方呢,freemarker的插件除了支持freemarker语法也支持html语句,而velocity的插件貌似只支持velocity的语法,html就只是用普通的文本来显示了,在这一点上freemarker占上风了

3。freemarker对jsptag的支持很好,算了,不到迫不得已还是不要这样做吧。

还有就是两者的语法格式,这一点上不同的人有不同倾向

下面就先介绍标签吧

一、freemarker模板文件主要有4个部分组成

  1、文本,直接输出的部分

  2、注释,即<#--...-->格式不会输出

  3、插值(interpolation):即${..}或者#{..}格式的部分,将使用数据模型中的部分替代输出

  4、ftl指令:freemarker指令,和html标记类似,名字前加#予以区分,不会输出。

  ftl指令规则

    freemarker有三种ftl标签,这和html的标签是完全类似的

     开始标签:<#directivename parameters>

     结束标签:</#directivename>

     空标签: <#directivename parameters />

     实际上,使用标签时前面的#符号也可能变成@,如果该指令是一个用户指令而不是系统内建指令时,应将#符号改为@符号

   插值规则

     freemarker的插值有如下两种类型

     1、通用插值:${expr}

     2、数字格式化插值:#{expr}或者#{expr;format}

    通用插值,有可以分为四种情况

    a、插值结果为字符串值:直接输出表达式结果

    b、插值结果为数字值:根据默认格式(#setting 指令设置)将表达式结果转换成文本输出。可以使用内建的字符串函数格式单个插值,例如

       <#setting number_format = "currency" />

       <#assign price = 42 />

       ${price}

       ${price?string}

       ${price?string.number}

       ${price?string.currency}

       ${price?string.percent}

   c、输出值为日期值:根据默认格式(由 #setting 指令设置)将表达式结果转换成文本输出,可以使用内建的字符串函数格式化单个插值,例如

      <#assign lastupdated = "2009-01-07 15:05"?datetime("yyyy-mm-dd hh:mm") />

      ${lastupdated?string("yyyy-mm-dd hh:mm:ss zzzz")};

      ${lastupdated?string("eee,mmm d,yy")};

      ${lastupdated?string("eeee,mmmm dd,yyyy,hh:mm:ss a '('zzz')'")};

      ${lastupdated?string.short};

      ${lastupdated?string.long};

      ${lastupdated?string.full};

   d、插值结果为布尔值

      <#assign foo=true />

      ${foo?string("是foo","非foo")}

   数字格式化插值

     数字格式化插值可采用#{expr;format}的形式来格式化数字,其中format可以是:

     mx:小数部分最小x位

     mx:小数部分最大x位

     例如:

        <#assign x = 2.582 />

        <#assign y =4 />

        #{x;m2};

        #{y;m2};

        #{x;m1};

        #{y;m1};

        #{x;m1m2};

        #{y:m1m2};

二、表达式

    表达式是freemarker的核心功能。表达式放置在插值语法(${...})之中时,表面需要输出表达式的值,表达式语法也可以与freemarker标签结合,用于控制输出

    1、直接指定值

      例如:

      a、字符串

         ${'我的名字是\"yeek\"'};

         ${"我的文件保存在d:\\盘"};

      b、数值

      c、布尔值

      d、日期型

         freemarker支持date、time、datetime三种类型,这三种类型的值无法直接指定,通常需要借助字符串的date、time、datetime三个内建函数进行转换才可以

           <#assign test1 = "2009-01-22"?date("yyyy-mm-dd") />;

           <#assign test2 ="16:34:43"?time("hh:mm:ss") />

           <#assign test2 = "2009-01-22 17:23:45"?datetime("yyyy-mm-dd hh:mm:ss") />

           ${test1?string.full}

      e、集合

        集合以方括号包括,各集合元素之间以英文逗号(,)分隔,看如下的示例:

      <#list["星期一",,["星期二",["星期三",["星期四",["星期五"] as x>

         ${s};

        </#list>

      f、map集合

         map对象使用花括号包括,map中的key-value对之间以英文冒号(:)隔开,多组key-value对之间以英文逗号(,) 隔开

           例如

           <#assign score = {"语文":78,"数学":83,"java":89} >

             <#list score?key as x>

              ${x}--->${score[x]};

             </#list>

      2、输出变量值

          freemarker的表达式输出变量时,这些变量可以是顶层变量,也可以是map对象中的变量,还可以是集合中的变量,并可以使用点(.)语法来访问java对象的属性

          a、顶层变量

             map root = new hashmap();

             root.put("name","wenchao");

             对应顶层变量,直接使用${variablename}来输出变量值,变量名只能是数字、字母、下划线、$、@和#的组合,并不能以数字开头

          b、输出集合元素

             如果需要输出集合元素,则可以根据集合元素的索引来输出元素。集合元素的索引以方括号指定。

             假设有集合对象为:["星期一","星期二","星期三","星期四","星期五","星期六"],该集合对象名为week, 如果需要输出星期三,则可以使用如下语法:

                ${week[2]}

             集合里的第一个元素的索引是0

          c、输出map元素

              这里的map对象可以是直接hashmap的实例,甚至包括 javabean实例,对应javabean实例,我们一样可以把其当成属性为key,属性为value的map实例

      3、字符串操作

         a、字符串链接

           字符串连接有两种语法

           a、使用${..}(或#{..})在字符串常量部分插入表达式的值,从而完成字符串连接

           b、直接使用连接运算符(+)来连接字符串

             使用第一种语法来连接字符串

             ${"hello,${user}!"}

              第二种使用连接符号来连接字符串

             ${"hello,"+user+"!"};

               值的注意的是,${..}只能用于文本部分,因此,下面的代码是错误的:

                 <#if ${isbig}>wow!</#if>

                 <#if "${isbig}">wow!</#if>

                 应该写成:

                 <#if isbig>wow!</#if>

         b、截取字符串

            map root = new hashmap();

            root.put("book","疯狂ajax讲义");

            ${book[0]}

            ${book[4]}

            ${book[1..4]}

      4、集合连接运算符

        这里所说的集合连接运算时将两个集合连接成一个新的集合,连接集合的运算符是+,例如

        <#list ["星期一"," 星期二","星期三"]+["星期四","星期五"] as x>

           ${x}

      5、map连接运算符

             map对象的连接运算也是将两个map对象连接成一个新的map对象,map对象的连接运算符是+。如果两个map对象具有相同的 key,则后加入map里的key所

         对应的value替代原来key所对应的value

      6、算术运算符

         freemarker表达式中完全支持算术运算,freemarker支持的算术运算符包括: +,-,*,/,%

         看如下代码示范

           <#assign x = 5 />

           ${x* -100}

           ${x/2}

           ${12%10}

         在表达式中使用算术运算时要注意以下几点。

         a、运算符两边的运算数必须是数字,因此下面的代码是错误的:

           ${3*"5"}

         b、使用+(既可以作为加号,也可以作为字符串连接运算符)运算时,如果一边是数字,一边是字符串,就会自动将数字转化为字符串。例如

            ${3+"5"}

            输出结果:35

         c、使用内建的int函数可对数值取整。例如

            <#assign x = 5>

            ${(x/2)?int}

            ${1.1?int}

            ${1.999?int}

            ${-1.9999?int}

            ${-1.1?int}

       7、比较运算符

           表达式中支持的比较运算符有如下几个

           a、=(或者==):判断两个值是否相等.

           b、!=:判断两个值是否不相等

           c、 >(或者gt):判断坐标值是否大于右边值

           d、 >=(或者gte):判断坐标值是否大于等于右边值

           e、 <(或者lt):判断左边值是否小于右边值

           f、 <=(或者lte):判断左边值是否小于等于右边值          

       8、逻辑运算符

          逻辑运算符有如下几个

          a、逻辑与:&&

          b、逻辑或:||

          c、逻辑非:!

          逻辑运算符只能作用于布尔值,否则将产生错误。

       9、内建函数

          freemarker还提供了一些内建函数来转换输出,可以在任何变量后紧跟?,?后紧跟内建函数,就可通过内建函数来转换输出变量

          下面是常用的内建的字符串函数

          a、html:对字符串进行html编码

          b、cap_first:将字符串第一个字母成大写

          c、lower_case:将字符串转换成小写

          d、upper_case:将字符串转换成大写

          e、trim: 去掉字符串前后的空白字符

          下面是集合的常用的内建函数

          a、size: 获得序列中元素的数目

          下面是数字值的常用的内建函数

          a、int 取得数字的整数部分

          例如

          <#assign test="tom & jerry" />

          ${test?html}

          ${test?upper_case?html}

       10、空值处理运算符

          freemarker对空值的处理非常严格,freemarker的变量必须有值,没有被赋值的变量就会抛出异常。

       11、运算符优先级

  三、freemarker 的常用指令

     1、if指令

        分支控制语句

        语法格式如下

       <#if condition>

            ....

       <#elseif condition2>

          ...

       <#elseif condition3>      

       <#else>

       </#if>

     2、switch、case、default、break指令

        <#switch value>

           <#case refvalue>

              ...

              <#bread>

           <#default>

        </#switch>

        虽然freemarker提供了switch指令,但它并不推荐使用switch指令来控制也输出,而是推荐使用freemarker的if..elseif..else 指令来替代它。

    3、list、break指令

    list指令时一个典型的迭代输出指令,用于迭代输出数据模型中的集合。list指令的语法格式如下:

     <#list sequence as item>

       ...

     </#list>

      除此之外,迭代集合对象时,还包括两个特殊的循环变量:

      a、item_index:当前变量的索引值。

      b、item_has_next:是否存在下一个对象

      也可以使用<#break>指令跳出迭代

      <#list ["星期一","星期二","星期三","星期四","星期五"] as x>

          ${x_index +1}.${x} <#if x_has_next>,</#if>

          <#if x = "星期四"><#break></#if>

      </#list>

      4、include 指令

        include指令的作用类似于jsp的包含指令,用于包含指定页,include指令的语法格式如下

         <#include filename [options]

          在上面的语法格式中,两个参数的解释如下

          a、filename:该参数指定被包含的模板文件

          b、options:该参数可以省略,指定包含时的选项,包含encoding和parse两个选项,encoding指定包含页面时所使用的解码集,而parse指定被

             包含是否作为ftl文件来解析。如果省略了parse选项值,则该选项值默认是true

     5、 import指令

        该指令用于导入freemarker模板中的所有变量,并将该变量放置在指定的map对象中,import 指令的语法格式如下

        <#import path as mapobject>

        在上面的语法格式中,path指定要被导入的模板文件,而mapobject是一个map对象名,通过这行代码,将导致path模板中的所有变量都被放置

        在mapobject中

        <#import "/lib/common.ftl" as com>

     6、noparse指令

         noparse指令指定freemarker不处理该指令里包含的内容,该指令的语法格式如下:

         <#noparse>

            ...

         </#noparse>

     7、escape、noescape指令

     8、assign指令

        它用于为该模板页面创建或替换一个顶层变量

     9、setting指令

        该指令用于设置freemarker的运行环境,该指令的语法格式如下:

        <#setting name = value>

        name 的取值范围包括如下几个

         locale :该选项指定该模板所用的国家/语言选项

         number_format:该选项指定格式化输出数字的格式

         boolean_format:该选项指定两个布尔值的语法格式,默认值是"true、false"

         date_format,time_format,datetime_format:该选项指定格式化输出日期的格式

         time_zone:  设置格式化输出日期时所使用的时区

     10、macro、nested、return指令

下面再介绍一个例子

java代码

public class templatetest {  

    /** 

     * @param args 

     */  

    public static void main(string[] args) throws exception{  

        /* 准备数据 */  

        map latest = new hashmap();  

        latest.put("url", "products/greenmouse.html");  

        latest.put("name", "green mouse");  

        map root = new hashmap();  

        root.put("user", "big joe");  

        root.put("latestproduct", latest);  

        root.put("number", new long(2222));  

        root.put("date",new date());  

        list listtest = new arraylist();  

        listtest.add("1");  

        listtest.add("2");  

        root.put("list",listtest);  

        templateengine freemarkerengine = (templateengine)templatefactory.getinstance().getbean("freemarker");  

        freemarkerengine.run(root);//使用freemarker模板技术  

        templateengine velocityengine = (templateengine)templatefactory.getinstance().getbean("velocity");  

        velocityengine.run(root);//使用velocity模板技术  

    }  

工厂类,用来得到模板引擎

public class templatefactory {  

    private static templatefactory instance;  

    private map objectmap;  

    static{  

        instance = new templatefactory();  

    public templatefactory() {  

        super();  

        this.objectmap = new hashmap();  

        synchronized (this) {  

            objectmap.put("freemarker", new freemarkertemplateengine(){  

                public string gettemplatepath() {  

                    return "template";  

                }  

            });  

            objectmap.put("velocity", new velocitytemplateengine());  

        }  

    public static templatefactory getinstance(){  

        return instance;  

     * 模仿spring的工厂 

     * @param beanname 

     * @return 

    public object getbean(string beanname){  

        return objectmap.get(beanname);  

引擎接口

public interface templateengine {  

    void run(map context)throws exception;  

模板引擎的实现使用method template模式,因为有两个实现,这两个实现又存在公共的逻辑,所以选择了这个模式

public abstract class abstracttemplateengine implements templateengine{  

    public abstract string gettemplatepath();  

    public abstract string gettemplate();  

    public abstract string getenginetype();  

    public void run(map context)throws exception{  

        if(constants.engine_type_freemarker.equals(getenginetype()))  

            executefreemarker(context);  

        else  

            executevelocity(context);  

    private void executefreemarker(map context)throws exception{  

        configuration cfg = new configuration();  

        cfg.setdirectoryfortemplateloading(  

                new file(gettemplatepath()));  

        cfg.setobjectwrapper(new defaultobjectwrapper());  

        cfg.setcachestorage(new freemarker.cache.mrucachestorage(20, 250));  

        template temp = cfg.gettemplate(gettemplate());  

        writer out = new outputstreamwriter(system.out);  

        temp.process(context, out);  

        out.flush();  

    private void executevelocity(map root)throws exception{  

        velocity.init();  

        velocitycontext context = new velocitycontext(root);  

        org.apache.velocity.template template = null;  

        template = velocity.gettemplate(gettemplatepath()+gettemplate());  

        stringwriter sw = new stringwriter();  

        template.merge( context, sw );  

        system.out.print(sw.tostring());  

这个是freemarker实现

public class freemarkertemplateengine extends abstracttemplateengine{  

    private static final string default_template = "freemarkerexample.ftl";  

     * 这个方法应该实现的是读取配置文件 

    public string gettemplatepath() {  

        return null;  

    public void run(map root) throws exception{  

        super.run(root);  

    public string gettemplate() {  

        // todo auto-generated method stub  

        return default_template;  

    public string getenginetype() {  

        return constants.engine_type_freemarker;  

}

这个是velocity实现

 public class velocitytemplateengine extends abstracttemplateengine{  

private static final string default_template = "velocityexample.vm";  

        return "/template/";  

    public void run(map map) throws exception{  

        super.run(map);  

        return constants.engine_type_velocity;  

以下是模板 

1,freemarker模板 

freemarker template test:  

string test-----------${user}-----------${number}-----------${latestproduct.url}-----------${latestproduct.name}  

condition test-----------  

<#if user == "big joe">  

list iterator-----------  

<#list list as aa>  

${aa}  

</#list>   

</#if>  

date test-----------${date?string("mmm/dd/yyyy")}    

2,velocity模板

java代码  

******************************************************************************************************************  

velocity template test:  

#if ($user == "big joe")  

#foreach( $aa in $list )  

$aa  

#end  

date test-----------${date} 

至此整个例子就结束了,这个例子比较直观的表现两种技术的应用