一、freemarker介绍
- FreeMarker 是一款 模板引擎
- 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件
- 模板编写为FreeMarker Template Language(FTL)
- 它是简单的,专用的语言,不是像PHP那样成熟的变成语言。那就意味着要准备数据在在真实编程语言中来显示,比如数据库查询和业务运算,之后模板显示以及准备好的数据。在模板中,可以更加专注于如何展现数据,而在模板之外可以专注于要展示什么数据
二、freemarker环境搭建&&快速入门
需要创建Spring Boot + Freemarker 工程用于测试模板
2.1、创建测试工程
-
创建一个freemark-demo的测试工程用于freemarker的功能测试与模板测试
-
POM文件如下所示
-
<?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"> <modelVersion>4.0.0</modelVersion> <groupId>com.coolman</groupId> <artifactId>freemarker-demo</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--freemarker springboot starter--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> </project>
-
2.2、配置文件
-
application.yml文件如下所示
-
server: port: 9527 spring: application: name: freemarker-demo # 指定服务名 freemarker: cache: false # 关闭缓存,方便测试 settings: template_update_delay: 0 #检查模板更新延迟时间,设置为0表示立即检查,如果时间大于0会有缓存不方便进行模板测试 suffix: .ftl #指定Freemarker模板文件的后缀名 template-loader-path: classpath:/templates #指定模板文件存放的位置
-
2.3、创建启动类
-
启动类如下所示
-
package com.coolman.freemarker; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class FreemarkerApplication { public static void main(String[] args) { SpringApplication.run(FreemarkerApplication.class, args); } }
-
2.4、创建模型类
-
在freemarker的测试工程下创建模型类型用于测试
-
package com.coolman.freemarker.model; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class Person { private String name; private Integer age; }
-
2.5、创建模板
-
在resources下创建templates,并创建模板文件freemarker.ftl文件(模板中的插值表达式最终会被freemarker替换成具体的数据)
-
编写一个简单的HTML模板
-
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello Freemarker!</title> </head> <body> <b>普通文本 String 展示:</b><br><br> Welcome to SuperCoolMan's home !! <br> I’m 99 years old this year !! <br> <hr> <hr> </body> </html>
-
2.6、创建测试类并测试
-
创建Controller类,添加数据,最后返回模板文件
-
测试1:
-
package com.coolman.freemarker.controller; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.io.FileWriter; import java.io.IOException; import java.util.HashMap; @RestController @RequestMapping("/freemarker") public class FreemarkerController { // 引入freemarker.template包下的Configuration类,用来获取指定的模板对象 @Autowired private Configuration configuration; /** * 通过模板文件,生成html文件 */ @GetMapping("/test") public String getHtml() { try { // 使用Configuration的getTemplate方法,指定模板文件,获取模板对象 Template template = configuration.getTemplate("HelloFreemarker.ftl"); // 绑定数据 // 数据1: HashMap<String, Object> map = new HashMap<>(); map.put("name", "SuperCoolMan"); map.put("age", 99); FileWriter fileWriter = new FileWriter("E:\\系统默认\\桌面\\news-init\\freemarker-demo\\src\\main\\resources\\testHtml\\HelloFreemarker.html"); template.process(map, fileWriter); return "SUCCESS!"; } catch (IOException | TemplateException e) { e.printStackTrace(); } return "ERROR!"; } }
-
浏览器打开生成的静态文件
-
-
-
修改Controller中的方法,添加Person对象数据
-
package com.coolman.freemarker.controller; import com.coolman.freemarker.model.Person; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.io.FileWriter; import java.io.IOException; import java.util.HashMap; @RestController @RequestMapping("/freemarker") public class FreemarkerController { // 引入freemarker.template包下的Configuration类,用来获取指定的模板对象 @Autowired private Configuration configuration; /** * 通过模板文件,生成html文件 */ @GetMapping("/test") public String getHtml() { try { // 使用Configuration的getTemplate方法,指定模板文件,获取模板对象 Template template = configuration.getTemplate("HelloFreemarker.ftl"); // 绑定数据 // 数据1: HashMap<String, Object> map = new HashMap<>(); map.put("name", "SuperCoolMan"); map.put("age", 99); // 数据2: map.put("person", new Person("超级猛男", 18)); FileWriter fileWriter = new FileWriter("E:\\系统默认\\桌面\\news-init\\freemarker-demo\\src\\main\\resources\\testHtml\\HelloFreemarker.html"); template.process(map, fileWriter); return "SUCCESS!"; } catch (IOException | TemplateException e) { e.printStackTrace(); } return "ERROR!"; } }
-
修改模板
-
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello Freemarker!</title> </head> <body> <b>普通文本 String 展示:</b><br><br> Welcome to ${name}'s home !! <br> I’m ${age} years old this year !! <br> <hr> <b>对象Person中的数据展示:</b><br/> 姓名:${person.name}<br/> 年龄:${person.age} <hr> </body> </html>
-
-
浏览器打开生成的静态文件
-
2.7、总结
三、freemarker语法指令
3.1、基础语法
3.1.1、注释
-
注释,即
<#-- -->
,介于其之间的内容会被freemarker忽略-
<#-- 我是一个freemarker的注释-->
-
3.1.2、插值
-
插值(Interpolation):即
${...}
部分,freemarker会用真实的值代替${}
(PS:如果返回的数据中没有该真实值,freemarker服务会直接报错)-
Welcome to ${name}'s home
-
3.1.3、FTL指令
-
FTL指令:和HTML标记类似,名字前加
#
予以区分,Freemarker会解析标签中的表达式或逻辑-
<# > FTL指令 </#>
-
3.1.4、文本
-
文本,仅文本信息,这些不是freemarker的注释、插值、FTL指令的内容会被freemarker忽略解析,直接输出内容
-
<#--freemarker中的普通文本--> 我是一个普通的文本
-
3.2、集合指令(List、Map)
接下来的测试,只截取部分代码,直接在原代码的基础上增加即可
-
为了Controller尽可能简略,创建一个
getData
方法,返回一个Map集合,Controller方法直接调用这个getData
生成测试数据即可-
private Map<String, Object> getData() { HashMap<String, Object> map = new HashMap<>(); // String类型: map.put("name", "SuperCoolMan"); map.put("age", 99); // Person类型: map.put("person", new Person("超级猛男", 18)); // return map; }
-
3.2.1、List集合
-
Controller
-
// List集合类型 ArrayList<Person> list = new ArrayList<>(); list.add(new Person("瞳夕", 18)); list.add(new Person("二呆", 18)); list.add(new Person("嘿嘿", 18)); map.put("wifeList", list);
-
-
模板
-
<b>List类型中的数据展示:</b><br/> <#list wifeList as wife> 序号:${wife_index + 1} <br/> <#--freemarker中FTL指令自带的序号属性,默认从0开始--> 姓名:${wife.name} <br/> 年龄:${wife.age} <br/><br/> </#list> <hr>
-
-
最终效果
3.2.2、Map集合
-
Controller
-
// Map集合类型 HashMap<String, Object> anOtherMap = new HashMap<>(); anOtherMap.put("smallWife1", new Person("莫妮卡", 18)); anOtherMap.put("smallWife2", new Person("玛丽莲梦露", 18)); map.put("smallWife", anOtherMap);
-
-
模板
-
<b>Map + Person 类型中的数据展示:</b><br/> <b>方式一:通过map['keyname'].property 输出对应信息:</b><br/> 性别:${wifeMap['smallWife1'].name} <br/> 年龄:${wifeMap['smallWife1'].age} <br/> <b>方式二:通过map.keyname.property 输出对应信息:</b><br/> 性别:${wifeMap.smallWife2.name} <br/> 年龄:${wifeMap.smallWife2.age} <br/> <hr>
-
-
最终效果
-
遍历Map集合
-
模板
-
<b>遍历Map中多个Person信息:</b><br/> <#list wifeMap?keys as key> 序号:${key_index} <br/> 性别:${key.name} <br/> 年龄:${key.age} <br/><br/> </#list> <hr>
-
-
最终效果
-
3.3、if指令
-
if
指令即判断指令,是常用的FTL指令,freemarker在解析的时候遇到if
会进行判断,条件为真则输出if
中间的内容,否则跳过内容不在输出-
指令格式
-
<#if >...</#if>
-
-
-
Controller
-
// if test map.put("score", 88);
-
-
模板
-
<b>if指令测试:</b><br/> <#if (score > 90)> 继续努力 <#elseif (score > 80)> 面包总会有的 <#else> 革命尚未成功,同志仍需努力 </#if> <hr>
-
-
最终效果
3.4、空值处理
3.4.1、判断某变量是否存在
-
语法格式
variable??
-
如果该变量存在,返回true,否则返回false
-
如下例所示
-
<#if stus??> <#list stus as stu> ...... </#list> </#if>
-
3.4.2、缺失变量默认值
- 使用
!
要指定一个默认值,当变量为空的时候显示默认值- 例:
${name!'默认值'}
表示如果name为空,显示默认值
- 例:
- 如果是嵌套对象则建议使用
()
括起来- 例:
${(stu.bestFriend.name)!"默认值"}
表示如果stu或者bestFriend或者name为空默认显示默认值
- 例:
3.5、内建函数
3.5.1、获取集合大小
- 模板中直接取值
${集合名?size}
3.5.2、日期格式化
- 后端返回一个Date对象,模板要取值,则如下所示
map.put("today",new Date())
- 显示年月日
${today?date}
- 显示时分秒
${today?time}
- 显示日期+时间
${today?datetime}
- 自定义格式化
${today?string("yyyy年MM月")}
3.5.3、内建函数c
(数字格式化)
- 后端返回一串数字,模板可以对其进行格式化,如下所示
map.put("point", 102920122);
- point是数字型,使用
${point}
会显示这个数字的值,每三位使用逗号隔开 - 如果不像显示为每三位分隔的数字,可以使用
c
函数将数字转成字符串输出 - 指令格式如下所示
${point?c}