前言
本篇文章主要介绍的是springboot整合freemarker填充ftl模板文件,生成新的文件(如html),以及freemarker的语法。
GitHub源码链接位于文章底部。
freemarker介绍
freemarker是一款模板引擎,它基于模板来生成文本输出。这里的文本包括但不限于html页面,word,各种源代码文本......
工作原理
模板:就是一份已经写好了基本内容,有着固定格式的文档,其中空出或者用占
位符标识的内容,由使用者来填充,不同的使用者给出的数据是不同的。在模板
中的占位符,在模板运行时,由模板引擎来解析模板,并采用动态数据替换占位
符部分的内容。
freemarker的应用方向有两个,一是基于ftl文件,将内容填充到ftl文件中,就可以使用制作ftl模板的文本的方式进行访问和显示了,比如使用html文本制作了一个ftl模板,我们使用代码填充数据进ftl模板,那么我们就能以访问html的方式去打开这个文件了;另一种方式则是直接生成对应的文件,比如生成xxx.html的文件。
应用场景:
淘宝中的商品数不胜数,在商品的详情页这一块,如果全都以真实的html页面显示,那么有多少个商品就得有多少个页面了,何况还有增删改的情况。所以使用一个固定的ftl模板,填充数据,这样一个文件就能显示无数个页面的内容了。
再比如一些政府单位的项目,每天需要发送一些word文档给领导,此时只需要通过程序将数据填充进ftl模板,然后生成一个个的xxx.word文件就行了。
ftl指令
1.一些常见的符号说明:
${}插值; 只能输出数值、日期或者字符串,其它类型不能输出。
在ftl页面中添加如下代码:
<#--这是 freemarker 注释,不会输出到文件中--> <h1>${name}; ${message}</h1>
在freemarker接口里添加数据如下:
这一段代码在页面输出的就是:
<#freemarker 命令
<#-- 注释 -->
<@使用自定义命令
??是判断对象是否存在
?函数调用
2.assign
此指令用于在页面上定义一个变量。 语法: <#assign name=value>
<#--assign--> <#--简单类型--> <#assign linkman="李四"/> 联系人:${linkman} <#assign info={"tel":"110","sex":"男"}/> 电话:${info.tel};性别:${info.sex}
页面输出如下:
3. include
语法: <#include path>
说明: path 参数可以是如 "foo.ftl"和 "../foo.ftl"一样的相对路径,或者是如"/foo.ftl"这样的绝对路径。
创建模板文件 header.ftl
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>嵌入页面</title> </head> <body> <h1>嵌入页面</h1> </body> </html>
在index模板文件中使用 include 指令引入 header.ftl 模板文件
<#--include--> <#include "header.ftl"/>
访问接口,在index.ftl视图如下:
4.if
<#if condition>
...
<#elseif condition2>
...
<#elseif condition3>
...
...
<#else>
...
</#if>
这里:
condition , condition2 等:表达式将被计算成布尔值。
关键字: gt :比较运算符“大于”; gte :比较运算符“大于或等于”; lt :比较
运算符“小于”; lte :比较运算符“小于或等于”
<#--if--> <#assign bool=false/> <#if bool> bool为true <#else> bool为false </#if>
这里定义一个bool为false,然后使用if进行判断,最后输出为false,
页面显示为:bool为false
5.list
语法:
<#list sequence as item>
...
</#list>
这里:
sequence :表达式将被算作序列或集合
item :循环变量(不是表达式)的名称
如果想在循环中得到索引,使用循环变量+_index 就可以得到。如上述语法中则可以使用 item_index 可以得到循环变量
在freemarker接口里添加数据如下:
在index.ftl中遍历
<#--list--> <#list goodsList as fruit> 索引:${fruit_index}, 水果:${fruit.name}; 价格:${fruit.price} </#list>
页面显示为:
6.内建函数
6.1使用 size 函数来实现对于集合大小的获取:
<#--获取集合总记录数--> 总共${goodsList?size}条记录 <#--集合中索引为1的name属性--> ${goodsList[1].name} <#--集合中第一个元素的name属性--> ${goodsList?first.name}
显示如下:“总共2条记录 香蕉 苹果 ”
6.2可以使用 eval 将 json 字符串转换为对象
<#assign str="{'id':'123','text':'文本'}"/> <#assign jsonObj=str?eval/> id为:${jsonObj.id};text为:${jsonObj.text}
显示如下:“id为:123;text为:文本”
6.3日期格式化
在freemarker接口里添加数据如下:
dataModel.put("today", new Date());
ftl页面添加:
当前日期:${today?date}<br> <hr> 当前时间:${today?time}<br> <hr> 当前日期+时间:${today?datetime} <hr> 格式化显示当前日期时间:${today?string('yyyy年MM月dd日 HH:mm:ss')}
显示如下:
6.4数字转换为字符串
在freemarker接口里添加数据如下:
dataModel.put("number", 123456789L);
index.ftl模板中添加:
<#--数值显示处理--> ${number}
显示如下:“123,456,789”
发现数字会以每三位一个分隔符显示,有些时候不需要这个分隔符,就需要将数
字转换为字符串,使用内建函数 c
${number?c}
显示如下:“123456789”
7.空值处理
在 FreeMarker 中对于空值必须手动处理。
${emp.name!} 表示 name 为空时什么都不显示
${emp.name!(“名字为空”)} 表示 name 为空时显示 名字为空
${(emp.company.name)!} 表示如果 company 对象为空则什么都不显示, !只用
在最近的那个属性判断;所以如果遇上有自定义类型(导航)属性时,需要使用括号
${bool???string} 表示:首先??表示判断 bool 变量是否存在,存在返回 true 否则 false,然后对返回的值调用其内置函数 string
<#if str??> 表示去判断 str 变量是否存在,存在则 true,不存在为 false
${strs!"strs空值的默认显示值"} <hr> <#if strs??> str变量存在 <#else > strs变量不存在 </#if>
显示如下:
8.运算符
8.1算数运算符
FreeMarker 表达式中完全支持算术运算,FreeMarker 支持的算术运算符包括:+, - ,
*, / , %
8.2逻辑运算符
逻辑运算符有如下几个:
逻辑与:&&
逻辑或:||
逻辑非:!
逻辑运算符只能作用于布尔值,否则将产生错误
8.3. 比较运算符
表达式中支持的比较运算符有如下几个:
1 =或者==:判断两个值是否相等.
2 !=:判断两个值是否不等.
3 >或者 gt:判断左边值是否大于右边值
4 >=或者 gte:判断左边值是否大于等于右边值
5 <或者 lt:判断左边值是否小于右边值
6 <=或者 lte:判断左边值是否小于等于右边值
=和!=可以用于字符串,数值和日期来比较是否相等,但=和!=两边必须是相
同类型的值,否则会产生错误,而且 FreeMarker 是精确比较,"x","x ","X"
是不等的.其
它的运行符可以作用于数字和日期,但不能作用于字符串,大部分的时候,使用 gt
等字母运算符代替>会有更好的效果,因为 FreeMarker 会把>解释成 FTL 标签的结
束字符,当然,也可以使用括号来避免这种情况,如:<#if (x>y)>
springboot整合freemarker
首先来看一下项目结构:
创建一个springboot项目
pom文件中添加依赖
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-parent</artifactId> <version>2.1.3.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> </dependencies>
application.yml中添加freemarker的相关配置
server: port: 8080 spring: freemarker: #设置编码格式 charset: utf-8 #设置文件后缀 suffix: .ftl #设置ftl文件路径 template-loader-path: classpath:/templates #关闭缓存,及时刷新,上线生产环境需要改为true cache: false
在resources目录中添加templates文件夹因为这在yml中配置了。
template文件夹中添加两个ftl模板文件,待会儿会用到,因为是创建html的文件,所以直接新建一个html文件,再将后缀改为ftl就行了。
index.ftl代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Freemarker 测试</title> </head> <body> <#--这是 freemarker 注释,不会输出到文件中--> <h1>${name}; ${message}</h1> <hr> <#--assign--> <#--简单类型--> <#assign linkman="李四"/> 联系人:${linkman} <#assign info={"tel":"110","sex":"男"}/> 电话:${info.tel};性别:${info.sex} <hr> <#--include--> <#include "header.ftl"/> <hr> <#--if--> <#assign bool=false/> <#if bool> bool为true <#else> bool为false </#if> <hr> <#--list--> <#list goodsList as fruit> 索引:${fruit_index}, 水果:${fruit.name}; 价格:${fruit.price}<br> </#list> <hr> <#--获取集合总记录数--> 总共${goodsList?size}条记录 <#--集合中索引为1的name属性--> ${goodsList[1].name} <#--集合中第一个元素的name属性--> ${goodsList?first.name} <hr> <#assign str="{'id':'123','text':'文本'}"/> <#assign jsonObj=str?eval/> id为:${jsonObj.id};text为:${jsonObj.text}<p></p> <hr> 当前日期:${today?date}<br> <hr> 当前时间:${today?time}<br> <hr> 当前日期+时间:${today?datetime} <hr> 格式化显示当前日期时间:${today?string('yyyy年MM月dd日 HH:mm:ss')} <hr> <#--数值显示处理--> ${number} <hr> ${number?c}<p></p> <hr> ${strs!"strs空值的默认显示值"} <hr> <#if strs??> str变量存在 <#else > strs变量不存在 </#if> </body> </html>
header.ftl代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>嵌入页面</title> </head> <body> <h1>嵌入页面</h1> </body> </html>
在freemarker接口中添加代码,返回freemarker的index.ftl视图
@RequestMapping("/freemarkerIndex") public String freemarker(Map<String, Object> dataModel) { dataModel.put("name", "张三"); dataModel.put("message", "hello world"); List<Map<String,Object>> list = new ArrayList<>(); Map<String, Object> map1 = new HashMap<>(); map1.put("name", "苹果"); map1.put("price", 4.5); Map<String, Object> map2 = new HashMap<>(); map2.put("name", "香蕉"); map2.put("price", 6.3); list.add(map1); list.add(map2); dataModel.put("goodsList", list); dataModel.put("today", new Date()); dataModel.put("number", 123456789L); return "index"; }
ps:这里dataModel的map只能从方法参数中获取,如果是自己new的去存储数据,填充到模板中,会报空值异常。
启动程序,通过localhost:8080/freemarkerIndex 访问该接口,得到页面如下:
上面这个接口是直接返回视图,
接下来是通过同样的数据,生成index.html静态文件。
因为这里是不用返回的,如果做成接口的话,虽然能成功生成文件,但是会报404,所以这里用的是单元测试,实际开发中,如果是返回的json格式,而不是视图,就可以做成接口。
@Test public void createFileByFreemarker() throws IOException, TemplateException { //创建配置对象 Configuration configuration = new Configuration(Configuration.getVersion()); //设置默认生成文件编码 configuration.setDefaultEncoding("utf-8"); //设置模板路径 configuration.setClassForTemplateLoading(this.getClass(), "/templates"); //获取模板 Template template = configuration.getTemplate("index.ftl"); //加载数据 Map<String, Object> dataModel =new HashMap<>(); dataModel.put("name", "张三"); dataModel.put("message", "hello world"); List<Map<String,Object>> list = new ArrayList<>(); Map<String, Object> map1 = new HashMap<>(); map1.put("name", "苹果"); map1.put("price", 4.5); Map<String, Object> map2 = new HashMap<>(); map2.put("name", "香蕉"); map2.put("price", 6.3); list.add(map1); list.add(map2); dataModel.put("goodsList", list); dataModel.put("today", new Date()); dataModel.put("number", 123456789L); //创建输出对象,将文件输出到D盘根目录下 FileWriter fileWriter = new FileWriter("D:/index.html"); //渲染模板和数据 template.process(dataModel, fileWriter); //关闭输出 fileWriter.close(); }
本文GitHub源码:https://github.com/lixianguo5097/springboot/tree/master/springboot-freemarker
CSDN:https://blog.csdn.net/qq_27682773
简书:https://www.jianshu.com/u/e99381e6886e
博客园:https://www.cnblogs.com/lixianguo
个人博客:https://www.lxgblog.com
来源:https://www.cnblogs.com/lixianguo/p/12519012.html