FreeMarker是一个很值得去学习的模版引擎。它是基于模板文件生成其他文本的通用工具。本章内容通过如何使用FreeMarker生成Html web 页面 和 代码自动生成工具来快速了解FreeMarker。
简介
FreeMarker是一款用java语言编写的模版引擎,它虽然不是web应用框架,但它很合适作为web应用框架的一个组件。
特点
- ==轻量级==模版引擎,不需要Servlet环境就可以很轻松的嵌入到应用程序中
- 能生成各种文本,如html,xml,java,等
- 入门==简单==,它是用java编写的,很多语法和java相似
FreeMarker 程序
这里通过模拟简单的代码自动生产工具来感受第一个FreeMarker程序。
项目目录结构
eclipse安装freemarker插件
maven依赖
1 | <dependency> |
hello.ftl模板(部分)
FreeMarkerDemo.java 核心方法,使用 FreeMarker 模版引擎。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88package com.freemarker.hello.templates;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.freemarker.hello.pojo.User;
import freemarker.template.Configuration;
import freemarker.template.Template;
/**
* 最常见的问题: java.io.FileNotFoundException: xxx does not exist. 解决方法:要有耐心
* FreeMarker jar 最新的版本(2.3.23)提示 Configuration 方法被弃用 代码自动生产基本原理: 数据填充
* freeMarker 占位符
*/
public class FreeMarkerDemo {
private static final String TEMPLATE_PATH = "src/main/java/com/freemarker/hello/templates";
private static final String CLASS_PATH = "src/main/java/com/freemarker/hello";
private static List<User> users = new ArrayList<User>();
static {
User u1 = new User("1", 22, "迟到峰");
User u2 = new User("2", 23, "要饭楚");
User u3 = new User("3", 27, "BUG李");
User u4 = new User("4", 25, "删库冬");
User u5 = new User("5", 29, "瓜子军");
User u6 = new User("6", 28, "老韩");
User u7 = new User(null, 25, null);
users.add(u1);
users.add(u2);
users.add(u3);
users.add(u4);
users.add(u5);
// users.add(null);
users.add(u6);
// users.add(u7);
// users.clear();
}
public static void main(String[] args) {
// step1 创建freeMarker配置实例
Configuration configuration = new Configuration(Configuration.VERSION_2_3_23);
Writer out = null;
try {
// step2 获取模版路径
configuration.setDirectoryForTemplateLoading(new File(TEMPLATE_PATH));
// step3 创建数据模型
Map<String, Object> dataMap = new HashMap<String, Object>();
dataMap.put("classPath", "com.freemarker.hello");
dataMap.put("htmlName", "使用FreeMarker生成html模板");
dataMap.put("helloWorld", "通过简单的 <代码自动生产程序> 演示 FreeMarker的HelloWorld!");
dataMap.put("author", "周宇峰");
dataMap.put("github", "github.com/542869246");
dataMap.put("name", "abcdefg");
dataMap.put("dateTime",new Date());
dataMap.put("users", users);
// step4 加载模版文件
Template template = configuration.getTemplate("hello.ftl");
// step5 生成数据
File docFile = new File(CLASS_PATH + "\\" + "hello.html");
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(docFile)));
// step6 输出文件
template.process(dataMap, out);
System.out.println("文件创建成功 !");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != out) {
out.flush();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
运行程序后刷新项目,会发现多了一个hello.html文件。
语法详解
数据类型
和java不同,FreeMarker不需要定义变量的类型,直接赋值即可。
字符串: value = “xxxx” 。 单引号和双引号是一样的。字符串中可以使用转义字符”\”。如果字符串内有大量的特殊字符,则可以在引号的前面加上一个字母r,则字符串内的所有字符都将直接输出。string = r”xxxx”。
数值:value = 1.2。数值可以直接等于,但是不能用科学计数法。
布尔值:true or false。
List集合:list = [1,2,3] ; list=[1..100] 表示 1 到 100 的集合,反之亦然。
Map集合:map = {“key” : “value” , “key2” : “value2”},key 必须是字符串
时间对象:
1 | root.put("date1",new Date()); |
JaveBean:Freemarker中对于javabean的处理跟EL表达式一致,类型可自动转化!非常方便!
注释:<#– abcd –>
字符串操作
声明变量和输出:
1 | <#assign name="zyf"> //声明一个变量值为zyf的变量name |
字符串连接:
1 | //使用嵌套或者+ 进行字符串连接操作 |
字符串截取:
1 | <#assign a="abcdefg"> |
string[index]。index 可以是一个值,也可以是形如 0..2 表示下标从0开始,到下标为2结束。一共是三个数。
substring(start,end)从一个字符串中截取子串。
start:截取子串开始的索引,start必须大于等于0,小于等于end。
end: 截取子串的长度,end必须大于等于0,小于等于字符串长度,如果省略该参数,默认为字符串长度。
算数运算:
支持”+”、”-”、”*”、”/“、”%”运算符
1 | <#assign number1=10 number2=5 > |
比较运算符
表达式中支持的比较运算符有如下几种:
- =(或者==):判断两个值是否相等;
- !=:判断两个值是否不相等;
注: =和!=可以用作字符串、数值和日期的比较,但两边的数据类型必须相同。而且FreeMarker的比较是精确比较,==不会忽略大小写及空格==。 - >(或者gt):大于
- >=(或者gte):大于等于
- <(或者lt):小于
- <=(或者lte):小于等于
1
2
3
4
5<#if number1 + number2 gte 12 || number1 - number2 lt 6>
"*" : ${number1 * number2}
<#else>
"/" : ${number1 / number2}
</#if>
上面这些比较运算符可以用于数字和日期,但不能用于字符串。大部分时候,使用==gt比>有更好的效果==,因为FreeMarker会把>解释成标签的结束字符。可以使用括号来避免这种情况,如:<#if (x>y)>。
逻辑运算符
- &&:逻辑与;
- ||:逻辑或;
- !:逻辑非
逻辑运算符只能用于布尔值。
内建函数
FreeMarker还提供了一些内建函数来转换输出,可以在任何变量后紧跟?,?后紧跟内建函数,就可以通过内建函数来轮换输出变量.下面是常用的内建的字符串函数
字符串相关常用的内建函数:
1 | <#assign data = "abcd1234"> |
集合相关常用的内建函数:
1 | size:获得集合中元素的个数 ${users?size} |
数字值相关常用的内建函数:
1 | <#assign floatData = 12.34> |
三元运算符
1 | ${(users?size gt 15)?string('a','b') } |
空值处理运算符
FreeMarker提供两个运算符来避免空值
- !:指定缺失变量的默认值
- ??:判断变量是否存在
1 | <#if user??> |
常用指令
if指令
这是一个典型的分支控制指令,该指令的作用完全类似于Java语言中的if,if指令的语法格式如下1
2
3
4
5
6<#assign age=23>
<#if (age>60)>老年人
<#elseif (age>40)>中年人
<#elseif (age>20)>青年人
<#else> 少年人
</#if>
switch case指令
switch(expr),其中expr只能是字符串、基本类型int或者包装类Integer,也包括不同的长度整型,例如short1
2
3
4
5
6
7
8
9
10
11
12
13<#switch being.size>
<#case "small">
This will be processed if it is small
<#break>
<#case "medium">
This will be processed if it is medium
<#break>
<#case "large">
This will be processed if it is large
<#break>
<#default>
This will be processed if it is neither
</#switch>
list指令
1 | //格式 |
List指令还隐含了两个循环变量:
item_index:当前迭代项在所有迭代项中的位置,是数字值。
item_has_next:用于判断当前迭代项是否是所有迭代项中的最后一项。
1 | <#list users as user> |
对List进行排序
- sort升序排序函数
sort对序列(sequence)进行排序,要求序列中的变量必须是:字符串(按首字母排序),数字,日期值。
1 | <#list list?sort as l>…</#list> |
- sort_by函数
sort_by有一个参数,该参数用于指定想要排序的子变量,排序是按照变量对应的值进行排序,如:
1 | <#list users?sort_by("age") as user>…</#list> |
age是User对象的属性,排序是按age的值进行的。
- reverse降序排序函数
1 | <#list list? reverse as l>…</#list>。 |
reverse使用同sort相同。reverse还可以同sort_by一起使用
如:想让用户按年龄降序排序,那么可以这个样写
1 | <#list users?sort_by("age")?reverse as user>…</#list> |
使用list指令遍历map
1 | //创建一个map,注意在freemarker中,map的key只能是字符串来作为key |
include指令
include指令的作用类似于JSP的包含指令,用于包含指定页
1 | 现在有hello.ftl、inc1.ftl与inc2.ftl 3个模板 |
总结:出现这种情况,在==两个模版中都分别存在变量名都相同的变量的时候,include包含进来,会进行覆盖==,include只时候将其公共的静态文件进行包含,而里面不涉及到内部函数以及变量声明之类的,当涉及到这种问题,我们就要用import进行导入
import指令
该指令用于导入FreeMarker模板中的所有变量,并将该变量放置在指定的Map对象中
1 | 接着上面 |
noparse指令
noparse指令指定FreeMarker不处理该指定里包含的内容,该指令的语法格式如下
1 | <#noparse>...</#noparse> |
assign指令
1 | <#assign name1=value1 name2=value2 ... nameN=valueN [in namespacehash]> |
虽然assign指定了这种复杂变量值的用法,但是我们也不要滥用这种用法,如下例子:
1 | <#assign x>Hello ${user}!</#assign> |
setting指令
该指令用于设置FreeMarker的运行环境,该指令的语法格式如下:
1 | <#setting name=value> |
在这个格式中,name的取值范围包含如下几个:
locale:该选项指定该模板所用的国家/语言选项
number_format:指定格式化输出数字的格式
boolean_format:指定两个布尔值的语法格式,默认值是true,false
date_format,time_format,datetime_format:指定格式化输出日期的格式
time_zone:设置格式化输出日期时所使用的时区
自定义指令(macro指令)(宏)
语法:
1 | <#macro name param1 param2 ... paramN> |
用例:
1 | //定义名为test的指令 |
function指令(函数)
用例:
1 | <#function buildPageUrl url pageNum data> |
参考文章(特别鸣谢):
https://blog.csdn.net/qq_34129814/article/details/76218863
https://segmentfault.com/a/1190000011768799
https://blog.csdn.net/fhx007/article/details/7902040/
https://www.cnblogs.com/qitian1/p/6463098.html