FreeMarker入门一
FreeMarker的官网,它提供了良好的中文文档。也可参考教程Java Web扩展-Freemarker
中文文档翻译请参考在线手册
为了方便写FreeMarker,可安装FreeMarker插件和定义一个FreeMarkerUtil
类
1.FreeMarker插件安装
在官网的Editor and IDE plugins
一节,找到安装说明
2.FreeMarkerUtil
类定义一些方便的方法,如获取模板和获取writer
获取模板:
public static Template geTemplate(String name)
{
Configuration cfg = new Configuration(Configuration.VERSION_2_3_26);
try {
//设置要加载模板的文件系统目录
cfg.setDirectoryForTemplateLoading(new File("src/ftl"));
Template template = cfg.getTemplate(name);
return template;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
获取writer
:
public static Writer getWrite(String name)
{
try {
Writer writer = new FileWriter(new File(name));
return writer;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
定义了之后,就可以很方便的生成HTML了
Template template = FreeMarkerUtil.geTemplate("basic.ftl");
Writer writer = FreeMarkerUtil.getWrite("basic.html");
try {
//使用提供的数据模型执行模板,将生成的输出写入到所提供的输出中
template.process(root, writer);
writer.flush();
} catch (TemplateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
入门
模板 + 数据模型 = 输出
模板
模板FreeMarker Template Language
(FTL),模板文件以ftl
为后缀,其组成:
- 文本: 包含HTML标签和静态文本内容,会原样输出
- 插值(interpolation):这部分输出会被计算的值替换,使用
${}
这种语法 - FTL标签:FTL标签也被称为 指令,标签的名字以
#
开头,是不会在输出中打印的(用户自定义的FTL标签则需要使用@
来代替#
) - 注释:使用
<#--
和-->
来标识。而HTML注释会输出在文档中
数据模型
数据模型的基本结构是树状的:
- hashes(哈希表或哈希):哈希表存储其他变量(被称为 子变量),可通过名称来查找
- scalars(标量):存储单值的变量
- sequences(序列):它们像哈希表那样存储子变量,但是子变量没有名字,它们只是列表中的项
数据类型和变量使用
模板中支持的数据类型有:
这里使用assign
来声明简单的变量,局部变量可以由 local
指令创建
标量
字符串
<#assign name="小王"> ${name}
数字
<#assign num=10> ${num}
布尔值,注意使用
?c
进行转换<#assign b=true> ${b?c}
日期
容器
哈希表,子变量通过名称来查找
<#assign map={"name": "小王", "age": 20}> ${map.name} ${map.age}
还可以通过
${map["age"]}
这种形式,支持有特殊字符序列,子变量通过一个整数来标识
<#assign seqs=[1, 2, 3, 4, 5]> ${seqs[1]}
上面的
seqs=[1, 2, 3, 4, 5]
可以使用seqs=1..5
来表示1到5
运算符
算数运算符
+
可以用来进行字符串的拼接,也可以进行序列的拼接
<#assign name="abc">
${name+"def"}
<#assign num=10+"a">
${num}
如下${num?int}
把小数转换为整数
<#assign num=10/3>
${num}<#-- 3.333 -->
<#assign num=10/3>
${num?int}<#-- 3 -->
比较运算符
lt
等同于<
lte
等同于<=
gt
等同于>
gte
等同于>=
字符串比较是否相同可直接使用==
<#assign eq = "a" == "a">
${eq?c}
空值处理运算符
如下的定义:
<#assign n={"name": "tom"}>
${n.age}
获取n.age
的值,但是没有定义这个key,所以会报错。
可使用${n.age!20}
来处理,表示如果n.age
存在就获取,不存在就是默认值20
插值
插值是用来插入具体指然后转换为文本(字符串),语法就是${}
注意插值的结果必须是字符串,数字或者日期类型
指令
条件指令
<#assign score=80>
<#if score lt 60>
差
<#elseif score lt 80>
中
<#elseif score lt 90>
良好
<#else>
优秀
</#if>
switch
<#assign level="A">
<#switch level>
<#case "A">
A
<#break>
<#case "B">
B
<#break>
<#case "C">
C
<#break>
<#default>
Default
</#switch>
循环指令
对序列循环
<#assign nums=[1, 3, 4, 5]>
<#list nums as num>
${num_index}, ${num}, ${num_has_next?c}
</#list>
${num_index}
获取索引
num_has_next
是一个bool值,表示是否有下一个值
上面的例子,其遍历结果是:
0, 1, true
1, 3, true
2, 4, true
3, 5, false
另一种使用方式是,对某个范围进行遍历
<#assign maxnum = 10>
<#list 1..maxnum as num>
${num}
<#if num==6>
<#break>
</#if>
</#list>
<#break>
用来终止循环
包含指令
语法为:
<#include path>
path 参数可以是如
"foo.ftl"
和"../foo.ftl"
一样的相对路径,或者是如"/foo.ftl"
这样的绝对路径。 相对路径是相对于使用import
指令的模板文件夹。 绝对路径是相对于程序员在配置FreeMarker
时定义的基路径 (通常指代”模板的根路径”)。
*/foo.ftl
这种添加*
的路径会一层层往上找
include
指令可以在一个模板中插入另一个模板的内容。
对于模板和引入的模板,它们的变量是可以共享的
如下的head.ftl
模板
<h1>${title}</h1>
把head.ftl
模板导入到include.ftl
模板中
<#assign title="这是标题">
<#include "head.ftl">
这是自身的内容
其它指令
noparse
指令表示FreeMarker 不会在这个指令体中间寻找FTL标签, 插值和其他特殊的字符序列,除了noparse
的结束标记。
compress
指令它捕捉在指令体(也就是在开始标签和结束标签中)中生成的内容, 然后缩小所有不间断的空白序列到一个单独的空白字符。
setting
指令设置影响FreeMarker
的值。可以设置国际化相关的东西
自定义指令
自定义指令可以将模板中重复的内容进行复用
定义:
- 自定义指令可以使用
macro
指令来定义或者使用纯Java
来实现 - 参数的声明,直接跟在指令名后,可以指定默认值
- 嵌套内容,使用
nested
指令
如下的一个简单的子定义指令
<#macro mydirect1>
这是自定义指令
</#macro>
<@mydirect1 />
两个要点:
1.使用macro
来声明自定的指令
2.使用自定义指令以@
开头
如下是带有参数的自定义指令,其中age
有默认值:
<#macro mydirect2 name age=18>
hello ${name}, age is ${age}
</#macro>
<@mydirect2 name="tom"/>
如果想要在自定义指令中嵌套一些内容,下面这样写是没有效果的:
<#macro mydirect3 >
mydirect3
</#macro>
<@mydirect3>
内容
</@mydirect3>
其显示的结果只有mydirect3
正确的做法是使用<#nested>
指令,如下
<#macro mydirect3 >
mydirect3
<#nested>
</#macro>
<@mydirect3>
内容
</@mydirect3>
其显示结果为:
mydirect3
内容
空值处理
1.null
对象的处理方法:使用!
如下,设置如下的数据:
User user = new User();
user.setName("小明");
//不设置group属性
//Group group = new Group();
//group.setName("朋友");
//user.setGroup(group);
Map<String, User> root = new HashMap<>();
root.put("user", user);
......
template.process(root, writer);
使用${user.group!"不属于任何组"}
可正常显示"不属于任何组"
但是使用${user.group.name!"不属于任何组"}
则会报错,这是因为!只会做最后一个属性的判读
整体加上一个小括号就不会出错了,如下:
${(user.group.name)!"不属于任何组"}
2.变量不存在的处理方式:使用??
(??
判断某个变量是否存在,返回boolean值)
<#if user.age??>
存在age
<#else>
不存在age
</#if>