FreeMaker学习笔记

         4年前,我一直都在QQ空间发布学习日志,想想那是自己真傻,为什么会选择在QQ空间发布呢,太不专业了,开始鄙视自己了。这是一篇有关FreeMarker的学习笔记。

         晚上回家不知道干嘛,就随便看了下FreeMaker开发指南这本电子书,大概看了一个多小时。不知道是文档写的太通俗了,还是我理解能力好,很快就上手写了些笔记和测试demo,觉得有必要做个记录。

       也许你会问FreeMaker是什么,它是干嘛用的?我就不去网上copy那些废话了,我的理解是它是一个基于Java的模版视图层组件,跟jsp一个性质,但是远比jsp强大。它有自己的指令和数据类型,虽然jsp也有jsp tag,但是FreeMaker的强大关键在于它的macro宏,而且用户还能自定义宏,什么叫宏?我的理解是,其实就是对模版代码片段的一个封装,类似于Java里的方法一个概念吧,个人观点哈!宏就是为了模版代码的重用。下面是我学习过程做的一些笔记,主要是一些指令,自己写些demo测试下就明白了,有过其他编程语言的,学它应该不难。
       FreeMaker常用指令:
条件判断
<#if 表达式>
<#elseif>
<#else>
</#if>
<#switch value>
 <#case refValue1>
   <#break>
 <#case refValue2>
   <#break>
 <#case refValueN>
   <#break>
 <#default>
</#switch>
集合遍历
<#list students as student>
引入外部文件
<#include "/top.html">
<#import "/lib/my_test.ftl" as my> 
include与import指令的区别:import导入方式有命名空间的概念,
比如这里的my就是命名空间,然后就可以通过my命名空间访问外部的宏
HTML代码原样输出 
<#escape x as x?html>
<#noescape></#noescape>刚好相反
定义变量
<#assign mail="
[email protected] " in my>
assign:定义变量的关键字,in my即在my命名空间内定义变量
compress指令:移除缩进、空行和重复的空格/制表符
<#compress></#compress>
!后面紧跟的是变量的默认值
${user!"Anonymous"}
??跟在变量后面,用于判断一个变量是否存在
<#if user??>
?if_exists  也是用于判断变量是否存在,如user.name?if_exists
?default(defaultValue)  用于设置变量默认值
!表达式 也可以用于设置默认值。如user.name!"Tom"
这里的()不能省,
若animals或python不存在,
则会报“未定义的变量”异常
(animals.python.price)!0
FreeMaker数据类型:
字符串 布尔值 数值 日期 序列 哈希值
FreeMaker内置函数:
String:
cap_first    首字母转换成大写
upper_case   转换成大写
lower_case   转换成小写
length       返回字符串长度
substring(start,end) 字符串截取,前闭后开区间
index_of(substring,start) 返回找到子串的第一个字符的索引,不存在返回-1
contains(str)  返回是否包含指定子串
number         将字符串转换为数字
replace(a,b)   将字符串中a替换成b
split(prefix)  将字符串那指定的分隔符分割成一个数组
trim           去除首尾空格
date("yyyy-MM-dd")  将字符串转换成指定格式日期
ends_with(str) 判断是否以指定子串结尾
html           字符串中所有的特殊HTML字符使用实体引用来代替,如< &lt
数字:
c                将数字转成字符串
string.number    数字转成字符串
string.currency  数字转换成货币形式字符串,如¥20.00
string. percent  数字转换成百分比,如20%
int              获取数字的整数部分
Hash(哈希类似于Java里的Map):
keys        返回hash的所有key,返回类型为序列(序列相当于数组的概念)
values      返回hash的所有value,返回类型为序列
Sequence(序列):
first    返回序列里的第一个元素
last     返回最后一个元素
reverse  将当前序列倒序排列
size     返回序列元素个数
sort     将序列的元素转换成字符串后排序
sort_by(property) 将序列按序列中对象的属性property进行排序
两个序列可以通过+合并,
sequence[5..9] sequence[5..] 序列截取,全闭区间
布尔值:
string   将布尔值转换成字符串
如true?string   结果"true"
string[("男", "女")]
宏:(类似于java里的方法,用于重用模版代码片段)
<#macro 宏名称 param1 param2...>
   宏定义体...
</#macro> 
宏调用:<@宏名称 param1="value1" param2="value2";a,b,c></@宏名称>或</@宏名称>
这里的a,b,c是循环变量
 
掌握了上面那些指令,就可以开始动手写些demo了。首先需要明确一点的是,FreeMaker是一个模版引擎,负责将ftl模版解析并输出成规定的格式文本文件或以流的形式传输,它与web容器无关,或者说它不依赖于Web环境。首先看看,在Java Project环境下如果使用它:
      首先新建个Java Project工程,new个测试类FreeMakerTest,首先FreeMaker模版需要一个FreeMaker模版管理器Configuration对象,该对象有个方法setDirectoryForTemplateLoading用于设置管理器默认以哪个目录作为根目录去加载模版文件,由于没有web环境,所以只能通过类加载器去获取当前类所处路径,然后相对当前类去确定模版文件路径,具体到代码如下:
Configuration cfg = new Configuration();
  cfg.setDirectoryForTemplateLoading(
      new File(
       FreeMakerTest.class.getClassLoader().getResource("com/yida/config/freemaker/").toURI()  
      ) 
  );
  cfg.setObjectWrapper(new DefaultObjectWrapper());
由于 Configuration 一般只需初始化一次即可,所以在非web环境中可以考虑实现成单实例,,如果是在web项目中,则可以把Configuration初始化放到Servlet的init方法里,这里我就懒得去实现了。然后就是获取模版、准备数据以及输出流输出解析内容,最后根据FreeMaker的模版+数据 = 输出的设计理念调用process执行即可,so easy!
//获取模版
  Template template = cfg.getTemplate("test.ftl");
  //创建数据模型  
        Map root = new HashMap(); 
        root.put("user", "Big Joe"); 
        Map latest = new HashMap(); 
        root.put("latestProduct", latest); 
        latest.put("url", "products/greenmouse.html"); 
        latest.put("name", "green mouse");
        Writer out = new OutputStreamWriter(System.out); 
        template.process(root, out); 
        out.flush(); 
然后建个ftl模版文件测试下效果,我test。ftl文件是放在com.yida.config.freemaker目录下,内容编辑如下:
        ${user}-${latestProduct.url}-${latestProduct.name}
直接运行FreeMakerTest java类,如果看到输出内容,则恭喜测试成功!下面开始试着写个web环境下的demo,GO!
     
首先也是新建个web项目,导入FreeMaker.jar,这个随便google下到处有下载。然后建个servlet测试类,init方法里初始化Configuration 管理器,然后doGet里通过configuration对象初始化web上下文,cfg.setServletContextForTemplateLoading方法接收两个参数,第一个参数就是servletcontext上下文,第二个参数是个String类型,用于设置加载template时的相对路径,如果第二个参数为null,则默认是相对webroot去加载ftl文件,假如你在webroot下有个ftl文件夹,该文件夹下存放的是ftl模版文件,那你cfg.getTemplate时,路径就是这样ftl/???.ftl,如果你设置相对路径是ftl,那这时候直接getTemplate里指定???.ftl即可。然后就是准备数据,接下来就是输出流的获取,只要是OutPutStream即可,当然你也可以给它一个FileOutPutStream,那样它就不是输出到浏览器显示了,而是写入到文件了,最后就是页面跳转到ftl文件,servlet中或者Action中输出流的获取就不多说了吧,其他的步骤大致相同,然后你注册下servlet类,最后新建个ftl模版文件测试下。
     上面介绍的是在web环境下用硬编码的形式使用FreeMaker来帮组我们解析模版并输出,其实FreeMaker已经帮我们实现好了一个Servlet工具类,我们只需注册它,然后我们访问任何ftl文件,都会经过它过滤处理即可,不用我们自己编码处理,省事很多。具体web.xml里配置看如下代码:
       <servlet>   
    <servlet-name>freemarker</servlet-name>   
    <servlet-class>   
        freemarker.ext.servlet.FreemarkerServlet    
    </servlet-class>   
     
    <init-param>   
        <param-name>NoCache</param-name>   
        <param-value>true</param-value>   
    </init-param>   
    <init-param>   
        <param-name>ContentType</param-name>   
        <param-value>text/html</param-value>   
    </init-param>   
    <!-- FreeMarker settings: -->   
    
    <init-param>   
        <param-name>default_encoding</param-name>   
        <param-value>UTF-8</param-value>   
    </init-param>   
   
    <init-param>   
        <param-name>locale</param-name>   
        <param-value>zh_CN</param-value>   
    </init-param>   
    <init-param>   
        <param-name>number_format</param-name>   
        <param-value>0.##########</param-value>   
    </init-param>    
   
    <load-on-startup>1</load-on-startup>   
</servlet>   
   
   
<servlet-mapping>   
    <servlet-name>freemarker</servlet-name>   
    <url-pattern>*.ftl</url-pattern>   
</servlet-mapping>   
上面红色标注部分是重点,
然后再写个Servlet测试类试验下,doGet里再也不用什么初始化模版管理器,获取模版、输出流等繁琐的工作了,只需通过request对象setAttribute往request作用域存放数据,然后request.getDispatcher进行页面跳转到ftl模版,这里不能使用redirect方式跳转,为什么我就不解释了吧,redirect的话request作用域的数据就丢失了,跳转到ftl就没意义咯。最后建个ftl测试,OK,若看到理想输出,则表明测试成功!
       下面介绍如何将FreeMaker与Struts2整合在一起,首先导入Struts2所需的jar和FreeMaker类库,然后web.xml里struts2注册。准备工作完毕后,那就new个测试类TestAction类别忘了继承下ActionSuport,写个测试方法test:
       public class TestAction extends ActionSupport{
    public String test(){
     ((HttpServletRequest)ActionContext.getContext().get(ServletActionContext.HTTP_REQUEST))
     .setAttribute("test", "Hello World!"); 
     return "test";
    }
}
然后struts.xml里配置下页面导航,这里需要注意的是result type需要写成
freemarker,注意是freemarker不是freemaker,我搞不清楚为什么Struts会犯这种弱智错误,弄的我调试N久,无语中......不清楚的请参看我下面配置代码:
       <action name="testAction" class="testAction">  
      <result type="freemarker" name="test">ftl/test.ftl</result>
     </action> 
test.ftl里很简单,就${test},如果没写错的话,最后访问Action的test方法浏览器会输出
Hello World!
      是不是觉得ftl模版写的太简单了,那就试着ftl里自定义个宏,说做就做,再建个ftl模版,内容编辑如下:
      <#macro test name>
    Hello ${name}!
</#macro>
<@test name="张三"/>  
    
 内容很简单,就是输出了个hello加上一个参数name值,然后本模版里调用宏,action测试类不需要更改,只需更改action导航配置,使它跳转新建的ftl模版即可。
      写到这里,我还是感觉不爽,因为自定义宏就是为了模版代码重用,也就是说宏的定义和宏的调用不应该杂糅在一个模版里,实际项目中比如我们可能会分页标签写个一个宏,其他页面调用该宏即可实现分页,幸好,FreeMaker import指令支持宏的动态导入。  做法如下:
      将宏定义所在模版里的宏调用语句删除,再新建个test2.ftl模版,通过import指令导入宏然后调用宏。test2.ftl内容编辑如下:
      <#import "/ftl/test.ftl" as t>
      <@t.test name="张三"/>
     上面的t相当于命名空间的概念,个人理解哈,as t是必须的,t可以自由命名。然后t.宏名调用,后面跟着的是参数传递,so easy!
然后更改action跳转到test2.ftl即可,最后测试,看到 Hello 张三!就表明测试成功。
       好,今天就到此为止吧!体会到FreeMaker的强大了没?可以 用FreeMaker来生成静态页面,可以用FreeMaker实现自定义标签,其实继承tagsuport也可以实现自定义标签,使用那种自己取舍,我建议使用FreeMaker,据说FreeMaker的解析性能很不错,而且FreeMaker实现了页面与数据及业务逻辑的独立分离,最最重要的是它强大的宏,这才是它受追捧的关键。Struts2的标签就是基于FreeMaker作为视图层组件的。

猜你喜欢

转载自iamyida.iteye.com/blog/2201816