1 FreeMarker介绍
- FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页、电子邮件、配置文件、源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
- FreeMarker是免费的,基于Apache许可证2.0版本发布。其模板编写为FreeMarker Template Language(FTL),属于简单、专用的语言。需要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,主要用于如何展现数据, 而在模板之外注意于要展示什么数据
1.1 常见的模板引擎
- JSP
- Thymeleaf
- Velocity
1.2 模板+数据模型=输出
- freemarker并不关心数据的来源,只是根据模板的内容,将数据模型在模板中显示并输出文件(通常为html,也 可以生成其它格式的文本文件)
1.2.1 数据模型
- 数据模型在java中可以是基本类型也可以List、Map、Pojo等复杂类型。
1.2.2 模板
1.2.3 输出
2 FreeMarker快速入门
- freemarker作为springmvc一种视图格式,默认情况下SpringMVC支持freemarker视图格式。
2.1 pom.xml
spring-boot-starter-freemarker
spring-boot-starter-web
lombok
okhttp
commons-io
spring-boot-starter-test
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>xc-framework-parent</artifactId>
<groupId>com.xuecheng</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../xc-framework-parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test-freemarker</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>
2.2 配置文件application.yml
server:
port: 8088 #服务端口
spring:
application:
name: test-freemarker #指定服务名
freemarker:
cache: false #关闭模板缓存,方便测试
settings:
template_update_delay: 0 #检查模板更新延迟时间,设置为0表示立即检查,如果时间大于0会有缓存不方便进行模板测试
2.3 创建模型类Student
@Data
@ToString
public class Student {
private String name;
private int age;
private Date birthday;
private Float money;
private List<Student> friends;
private Student bestFriend;
}
2.4 创建模板src/resources/templates/test1.ftl
- 在
src/main/resources
下创建templates,此目录为freemarker的默认模板存放目录。 - 在templates下创建模板文件test1.ftl,模板中的
${name}
最终会被freemarker替换成具体的数据。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf‐8">
<title>Hello World!</title></head>
<body>
Hello ${name}!
</body>
</html>
2.5 创建controller
@RequestMapping("/test1")
public String test1(Map<String, Object> map) {
//map形参存在request域中,是freemarker模板使用的数据
map.put("name", "张三");
//返回freemarker模板的位置,基于resource/templates路径
return "test1";
}
2.6 创建启动类
@SpringBootApplication
public class FreemarkerTestApplication {
public static void main(String[] args) {
SpringApplication.run(FreemarkerTestApplication.class, args);
}
}
3 FreeMarker基础
3.1 核心指令
3.1.1 数据模型
- Freemarker静态化依赖数据模型和模板,下边定义数据模型。下边方法形参map即为freemarker静态化所需要的数据模型,在map中填充数据:
@RequestMapping("/test1")
public String test1(Map<String, Object> map) {
//String
//map形参存在request域中,是freemarker模板使用的数据
map.put("name", "张三");
Student stu1 = new Student();
stu1.setAge(18);
stu1.setBirthday(new Date());
stu1.setMoney(200F);
stu1.setName("小红");
Student stu2 = new Student();
stu2.setAge(19);
stu2.setBirthday(new Date());
stu2.setMoney(100F);
stu2.setName("小方");
List<Student> friends = new ArrayList<>();
friends.add(stu1);
stu2.setFriends(friends);
stu2.setBestFriend(stu1);
List<Student> stus = new ArrayList<>();
stus.add(stu1);
stus.add(stu2);
//list
//将学生列表放在数据模型中
map.put("stus", stus);
Map<String, Student> stuMap = new HashMap<>();
stuMap.put("stu1", stu1);
stuMap.put("stu2", stu2);
//向数据模型放数据
//model
map.put("stu1", stu1);
//map
map.put("stuMap", stuMap);
//存放数字
//number
map.put("point", 123456789);
//返回freemarker模板的位置,基于resource/templates路径
return "test1";
}
3.1.2 基础指令
3.1.2.1 注释
<#--被注释的内容-->
3.1.2.2 插值
${name}
3.1.2.3 FTL指令
<#if></#if>
3.1.3 List指令
遍历数据模型中的list学生信息,(stus)
<table>
<tr>
<td>序号</td>
<td>姓名</td>
<td>年龄</td>
<td>金额</td>
<td>出生日期</td>
</tr>
<#list stus as stu>
<tr>
<td>${stu_index+1}</td>
<td>${stu.name}</td>
<td>${stu.money}</td>
<td>${stu.age}</td>
<td>${stu.birthday?date}</td>
</tr>
</#list>
</table>
- 注意:
[item]_index
得到循环的下标,值从0开始。
3.1.4 遍历Map数据
<!-- 第一种方法:在中括号中填写map的key -->
姓名:${stuMap['stu1'].name}<br>
年龄:${stuMap['stu1'].age}<br>
<!-- 第二种方法:在map后直接“点key” -->
姓名:${stuMap.stu2.name}<br>
年龄:${stuMap.stu2.age}<br>
<table>
<tr>
<td>序号</td>
<td>姓名</td>
<td>年龄</td>
<td>金额</td>
</tr>
<!-- 遍历map中的key, stuMap?keys是key列表(是一个list) -->
<#list stuMap?keys as k>
<tr>
<td>${k_index+1}</td>
<td>${stuMap[k].name}</td>
<td>${stuMap[k].age}</td>
<td>${stuMap[k].money}</td>
</tr>
</#list>
</table>
3.1.5 if指令
- if 指令即判断指令,是常用的FTL指令,freemarker在解析时遇到if会进行判断,条件为真则输出if中间的内容,否 则跳过内容不再输出。
<!-- 如果stus存在-->
<#if stus?? >
<#list stus as stu>
<tr>
<td>${stu_index+1}</td>
<!-- 如果name为小方,背景色为蓝色-->
<td <#if stu.name == "小方"> style="background: cadetblue"</#if>>${stu.name}</td>
<td>${stu.money}</td>
<!-- 大于号> 会和标签的尖括号混淆,可以用括号(stu.money > 300) 括起来,或者用gt代替>-->
<!-- 如果money大于150,背景色为蓝色-->
<td <#if stu.money gt 150> style="background: cadetblue"</#if>>${stu.age}</td>
<td>${stu.birthday?date}</td>
</tr>
</#list>
</#if>
3.2 其他指令
3.2.1 运算符
3.2.1.1 算数运算符
- +
- -
- *
- /
- %
3.2.1.2 逻辑运算符
- 与:
&&
- 或:
||
- 非:
!
3.1.2.3 比较运算符
=
==
!=
>
或gt
>=
或gte
<
或lt
<=
或lte
注意:
=
和!=
可以用于字符串,数值和日期来比较是否相等,但=
和!=
两边必须是相同类型的值,否则会产生错误- 使用
gt
等字母运算符代替>
会有更好的效果,因为 FreeMarker会把>
解释成FTL标签的结束字符,当然,也可以使用括 号来避免这种情况
<#if (x>y)>
3.2.2 空值处理
- 判断某变量是否存在使用
??
。用法为:variable??,如果该变量存在,返回true
,否则返回false
<#if stus?? >
...
</#if>
- 缺失变量默认值使用
!
使用!
要以指定一个默认值,当变量为空时显示默认值。
<!-- 如果name为空显示空字符串-->
${name!''}
<!-- 如果是嵌套对象则用()括起来-->
${(stuMap['stu1'].name)!''}
3.2.3 内建函数
- 语法:
变量?函数名称
3.2.3.1 集合的大小
${集合名?size}
3.2.3.2 日期格式化
显示年月日: ${today?date}
显示时分秒: ${today?time}
显示日期+时间: ${today?datetime}
自定义格式化: ${today?string("yyyy年MM月")}
3.2.3.3 c (数字不用逗号分隔)
<!--c函数将数字型转为字符串,123,456,789 -> 123456789-->
${point} 123,456,789
${point?c} 123456789
3.2.4 json转为对象 assign eval
assign
定义变量eval
转化为对象
<#--json转对象-->
<#assign text="{'name':'json_name','age':'20'}" />
<#assign data=text?eval />
name:${data.name} <br>
age:${data.age} <br>
4 静态化
4.1 使用模板文件静态化
//基于ftl模板生成html文件
@Test
public void testGenerateHtml() throws IOException, TemplateException {
//1. 定义配置类
Configuration configuration = new Configuration(Configuration.getVersion());
//2. 定义模板
//2.1 得到classpath的路径
String classpath = this.getClass().getResource("/").getPath();
//2.2 定义模板路径
configuration.setDirectoryForTemplateLoading(new File(classpath + "/templates/"));
//2.3 获取模板文件的内容
Template template = configuration.getTemplate("test1.ftl");
//3. 定义数据模型
Map map = getMap();
//4. 静态化
String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);
// System.out.println(content);
InputStream inputStream = IOUtils.toInputStream(content);
FileOutputStream outputStream = new FileOutputStream(new File("d:/prac_project/test1.html"));
//输出文件
IOUtils.copy(inputStream, outputStream);
inputStream.close();
outputStream.close();
}
4.2 使用模板字符串静态化(建议)
//根据模板内容(字符串)生成html
@Test
public void testGenerateHtmlByString() throws IOException, TemplateException {
//1. 定义配置类
Configuration configuration = new Configuration(Configuration.getVersion());
//2. 定义模板
//2.1 模板内容(字符串)
String templateString = "" +
"<html>\n" +
" <head></head>\n" +
" <body>\n" +
" 名称:${name}\n" +
" </body>\n" +
"</html>";
//2.2 使用模板加载器将字符串变为模板
StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
stringTemplateLoader.putTemplate("template", templateString);
//2.3 在配置中设置模板加载器
configuration.setTemplateLoader(stringTemplateLoader);
//2.4 获得模板
Template template = configuration.getTemplate("template", "utf-8");
//3. 定义数据模型
Map map = getMap();
//4. 静态化
String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);
// System.out.println(content);
InputStream inputStream = IOUtils.toInputStream(content);
FileOutputStream outputStream = new FileOutputStream(new File("d:/prac_project/test1.html"));
//输出文件
IOUtils.copy(inputStream, outputStream);
inputStream.close();
outputStream.close();
}
//获取数据模型
public Map getMap() {
Map map = new HashMap();
//map形参存在request域中,是freemarker模板使用的数据
map.put("name", "张三");
Student stu1 = new Student();
stu1.setAge(18);
stu1.setBirthday(new Date());
stu1.setMoney(200F);
stu1.setName("小红");
Student stu2 = new Student();
stu2.setAge(19);
stu2.setBirthday(new Date());
stu2.setMoney(100F);
stu2.setName("小方");
List<Student> friends = new ArrayList<>();
friends.add(stu1);
stu2.setFriends(friends);
stu2.setBestFriend(stu1);
List<Student> stus = new ArrayList<>();
stus.add(stu1);
stus.add(stu2);
//将学生列表放在数据模型中
map.put("stus", stus);
Map<String, Student> stuMap = new HashMap<>();
stuMap.put("stu1", stu1);
stuMap.put("stu2", stu2);
//向数据模型放数据
map.put("stu1", stu1);
map.put("stuMap", stuMap);
//存放数字
map.put("point", 123456789);
return map;
}