freemarker
- 1.简介
- 2.springboot集成freemarker
- 3.freemarker数据类型
- 3.1.布尔型
- 3.2.日期型
- 3.3.日期型
- 3.4.字符串型
- 3.5.序列类型
- 3.6.hash类型
- 4.freemarker常用指令
- 4.1.assign指令
- **4.2.逻辑判断指令**
- 4.3.list遍历指令
- 4.4.自定义指令 宏 macro
- 4.5.import导入指令
- 4.6.include包含指令
- 5.运算符
- 5.1.算术运算符
- 5.2.逻辑运算符
- 5.3.比较运算符
- 5.4.空值运算符
- 6.freemarker静态化
1.简介
FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
模板编写为FreeMarker Template Language (FTL)。它是简单的,专用的语言, 不是 像PHP那样成熟的编程语言。 那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据。
FreeMarker最初的设计,是被用来在MVC模式的Web开发框架中生成HTML页面的,它没有被绑定到 Servlet或HTML或任意Web相关的东西上。它也可以用于非Web应用环境中。
freemarker并不关心数据的来源,只是根据模板的内容,将数据模型在模板中显示并输出文件(通常为html,也可以生成其它格式的文本文件)。
springBoot虽然支持JSP,但是官方不建议使用,所以要学习下freemarker。
🤔总的来说,模板和数据模型是FreeMarker来生成输出(比如第一个展示的HTML)所必须的:
模板 + 数据模型 = 输出
中文手册:http://freemarker.foofun.cn/toc.html
2.springboot集成freemarker
2.1.创建springboot web项目,并引入freemarker的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
2.2.编写配置文件
还有很多其他的配置选项,但是一般不需要配置太多,配置个后缀名就够了,需要其他的再添
server:
port: 8080
spring:
freemarker:
#开启 freemarker 功能,默认为true
enabled: true
#页面模板后缀名
suffix: .ftl
#页面模板位置(默认为 classpath:/templates/)
template-loader-path: classpath:/templates/
#..................
2.3.在resources创建包templates,此目录为freemarker的默认模板存放目录。
2.4.编写模板,文件后缀名为 .ftl
模板(FTL编程)是由如下部分混合而成的:
- 文本:文本会照着原样来输出。
- 插值:这部分的输出会被计算的值来替换。插值由
${ and }
所分隔。 - FTL 标签:FTL标签和HTML标签很相似,但是它们却是给FreeMarker的指示, 而且不会打印在输出内容中。
- 注释:注释和HTML的注释也很相似,但它们是由
<#-- 和 -->
来分隔的。注释会被FreeMarker直接忽略, 更不会在输出内容中显示。
支持el表达式,区分大小写
5.controller层
return的结果要和模板名相同
@Controller
public class FMController {
@RequestMapping("/hello")
public String helloPage(Model model){
model.addAttribute("name", "lvhan");
return "hello";
}
}
启动后:
❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️
3.freemarker数据类型
- 布尔型: 等价于 Java 的 boolean 类型,不同的是不能直接输出,可转换为字符串输出
- 日期型: 等价于java 的 Date 类型,不同的是不能直接输出,需要转换成字符串再输出
- 数值型:等价于java 中的 int,float,double 等数值类型,可以转换为: 数值型(默认)、货币型、百分比型的形式
- 字符型: 等价于java 中的字符串,有很多内置函数
- sequence (序列)类型: 等价于java 中的数组,List,Set 等集合类型
- hash 类型: 等价于java 中的 Map 类型
3.1.布尔型
在controller中准备数据
model.addAttribute("flag",true);
写入模板
<#--布尔类型不能直接展示,要转换为字符串-->
<h1>布尔类型的展示:</h1>
<#--方式一:?c-->
${flag?c} <br>
<#--方式二:?then("true时对应的文本","false时对应的文本")-->
${flag?then("yes","no")} <br>
布尔类型不能直接展示,要转换为字符串
3.2.日期型
在controller中准备数据
model.addAttribute("date",new Date());
写入模板
<#--日期类型不能直接展示,要转换为字符串或者日期型-->
<h1>日期类型的展示:</h1>
<#--年月日?date-->
${date?date} <br>
<#--时分秒?time-->
${date?time} <br>
<#--年月日时分秒?datetime-->
${date?datetime} <br>
<#--指定格式,y:年 m:月 d:日 h:时 m:分 s:秒-->
${date?string("yyyy年mm月dd日 hh时mm分ss秒")} <br>
日期类型不能直接展示,要转换为字符串或者日期型
可以指定日期的显示格式
3.3.日期型
在controller中准备数据
model.addAttribute("age",19);
model.addAttribute("salary",18000);
model.addAttribute("avg",0.467);
写入模板
<h1>数值类型的展示:</h1>
<#--数值类型可以直接输出-->
${age}${salary}${avg} <br>
<#--转字符串 ?c普通字符串-->
${age?c} <br>
<#--货币型字符串 ?string.currency-->
${salary?string.currency} <br>
<#--百分比型字符串 ?string.percent 会自动四舍五入-->
${avg?string.percent} <br>
<#--保留浮点数指定小数位-->
${avg?string["0.#"]} <br>
数值类型可以直接输出,也可以转为货币型和百分比型,还能规定保留几位小数
3.4.字符串型
在controller中准备数据
model.addAttribute("str01","Hello2025");
model.addAttribute("str02","hi2025");
写入模板
<h1>字符串类型的展示:</h1>
<#--字符串类型可以直接输出-->
${str01}----${str02} <br>
<#--截取字符串 (左闭右开)?substring(start,end)-->
${str01?substring(1,4)} <br>
<#--首字母小写输出 ?uncap_first-->
${str01?uncap_first} <br>
<#--首字母大写输出 ?cap_first-->
${str02?cap_first} <br>
<#--字母转小写输出 ?lower_case-->
${str01?lower_case} <br>
<#--字母转大写输出 ?upper_case-->
${str02?upper_case} <br>
<#--获取字符串长度-->
${str02?length} <br>
<#--是否以指定字符开头,返回布尔类型,需要转字符串-->
${str01?starts_with("H")?string} <br>
<#--是否以指定字符结尾,返回布尔类型,需要转字符串-->
${str01?ends_with("25")?string} <br>
<#--获取指定字符的索引-->
${str02?index_of("i")} <br>
<#--去除字符串前后空格-->
${str02?trim} <br>
<#--替换指定字符串-->
${str02?replace("hi","cum")} <br>
字符串型可以直接输出,有很多方法
字符串空值情况处理:
在controller中准备数据,其中a值为null,b为空字符串
model.addAttribute("a",null);
model.addAttribute("b","");
写入模板
<h2>字符串空值情况处理</h2>
<#--如果字符串为null会报错,变量不存在也会报错,为空可以-->
${b} <br>
<#--!:指定缺失变量的默认值-->
${a!"yes"} <br>
<#--??:判断变量是否存在,返回布尔值,需要转为字符串-->
${b???string}
如果字符串为null会报错,变量不存在也会报错,为空可以
!:指定缺失变量的默认值
??:判断变量是否存在,返回布尔值,需要转为字符串
3.5.序列类型
在controller中准备数据
String[] strArr=new String[]{"小男娘1","小男娘2","小男娘3"};
model.addAttribute("strArr",strArr);
User[] userArr=new User[]{new User(18,"小男娘1"),new User(21,"小男娘2"),new User(19,"小男娘3")};
model.addAttribute("userArr",userArr);
写入模板
public class User {
private int age;
private String name;
public User() {
}
public User(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
<h1>序列类型的展示:</h1>
<#list strArr as str>
员工:${str} -- 索引: ${str?index} <br>
</#list>
--------<br>
获取序列的长度:${strArr?size}<br>
获取序列的第一个元素:${strArr?first}<br>
获取序列的最后一个元素:${strArr?last}<br>
--------<br>
倒序输出序列<br>
<#list strArr?reverse as str>
员工:${str} -- 索引: ${str?index} <br>
</#list>
--------<br>
升序输出<br>
<#list strArr?sort as str>
员工:${str} -- 索引: ${str?index} <br>
</#list>
--------<br>
降序输出<br>
<#list strArr?sort?reverse as str>
员工:${str} -- 索引: ${str?index} <br>
</#list>
--------<br>
<br>
<#list userArr as user>
员工:${user.name} --年龄:${user.age}-- 索引: ${user?index} <br>
</#list>
--------<br>
对user数组按照年龄进行排序<br>
<#list userArr?sort_by("age") as user>
员工:${user.name} --年龄:${user.age}-- 索引: ${user?index} <br>
</#list>
<#list 数组名 as 变量名></#list>
list和set差不多类似的指令
3.6.hash类型
对应java里的map
在controller中准备数据
Map<String,String> citymap=new HashMap<>();
citymap.put("beijing","北京");
citymap.put("shanghai","上海");
citymap.put("shenzhen","深圳");
model.addAttribute("citymap",citymap);
写入模板
<h1>hash类型的展示:</h1>
<#--遍历key得到value-->
<#list citymap?keys as key>
城市:${key} -- ${citymap[key]} <br>
</#list>
--------<br>
<#--直接遍历value-->
<#list citymap?values as value>
城市:${value} <br>
</#list>
可以直接遍历key再得到value,也可以直接遍历value
4.freemarker常用指令
4.1.assign指令
有时候我们需要在页面中定义数据使用,那么就需要assign指令
使用**assign**
指令可以创建一个新的变量,或者替换一个已经存在的变量
写入模板
<#--自定义变量-->
<#assign str="lvhan">
${str}<br>
<#--定义多个变量-->
<#assign num=1 names=["a","b","c","d","e","f"]>
${num}<br>
${names?join("~~~")}<br>
使用join函数把数组中的元素遍历出来,并用join函数中的符号把他们分隔开
4.2.逻辑判断指令
<#if>,<#elseif>,<#else>
写入模板
<h1>逻辑判断指令:</h1>
<#assign score=72>
<#if score lt 60>
不及格!
<#elseif score == 60>
及格!
<#elseif score gt 60 && score lt 80>
良好!
<#else>
优秀!
</#if><br>
<#if b??>
数据存在
<#else>
数据不存在
</#if>
4.3.list遍历指令
<h1>list遍历指令:</h1>
<#assign names=["a","b","c","d"]>
<#if names??>
<#list names as name>
${name}
</#list>
</#if> <br>
<#assign names2=[]>
<#list names2 as name>
${name}
<#else>
您需要遍历的数组中没有元素!
</#list>
当list中没有选项时,执行else指令
4.4.自定义指令 宏 macro
相当于java里的方法<#macro 指令名></#macro>
调用:<@指令名></@指令名>
占位命令:
<#nested>
,相当于占位符,一般结合macro指令一起用
在调用时,标签内写占位的内容,<@指令名>占位的内容</@指令名>
<h1>自定义指令 宏:</h1>
<#--就像是java里的方法-->
<#macro a>
<h3>The boy with the thorn in his side</h3>
</#macro>
<@a></@a>
<#macro b num1 num2>
${num1} ----- ${num2}
</#macro>
<@b num1=100 num2=200></@b><br>
<#macro c num>
<#list 1..num as i>
<#list 1..i as j>
${i} * ${j} = ${i * j}
</#list>
<br>
</#list>
</#macro>
<@c 7></@c>
<#macro d>
公司: <#nested >
</#macro>
<@d>美团</@d><br>
<@d>饿了吗</@d>
4.5.import导入指令
新建一个模板文件,里面写入macro指令
原来的模板文件:
<h1>import导入指令:</h1>
common里的宏指令:
<#import "common.ftl" as common>
<@common.c 9></@common.c>
相当于java中的包,导入别的包使用
4.6.include包含指令
作用:在模板中包含另一个模板(可以是html,ftl,txt等)
可以使用 include 指令在你的模板中插入另外一个 FreeMarker 模板文件。被包含模板的输出格式是在 include 标签出现的位置插入的。 被包含的文件和包含它的模板共享变量,就像是被复制粘贴进去的一样。
<h1>include包含指令:</h1>
<#include "testInclude.ftl">
<#include "testInclude.html">
<#include "testInclude.txt">
5.运算符
5.1.算术运算符
‘+’,‘-’,‘*’,‘/’
加减乘除都跟其他的一样,但是要写在插值里才有用
5.2.逻辑运算符
‘&&’,‘||’,‘!’
跟java的一样,且 或 非
5.3.比较运算符
- gt:大于
- lt:小于
- gte:大于等于
- lte:小于等于
- ==:等于
- !=:不等于
5.4.空值运算符
看上边字符串的空值处理
6.freemarker静态化
用户访问动态页面时都需要通过ajax发送请求查询数据库获取动态数据进行展示,但是这页面的访问量如果比较大且数据库中的数据变化频率并不高,这就会对数据库造成了很大的访问压力。如何对数据库减压并提高系统运行性能呢?答案就是页面静态化。
页面静态化其实就是将原来的动态网页(例如通过ajax请求动态获取数据库中的数据并展示的网页)改为通过静态化技术生成的静态网页,这样用户在访问网页时,服务器直接给用户响应静态html页面,没有了动态查询数据库的过程。
生成工具类
@Component
public class GenUtil {
/**
* 根据模板,利用提供的数据,生成文件
*
* @param sourceFile 模板文件,带路径
* @param data 数据
* @param aimFile 最终生成的文件,若不带路径,则生成到当前项目的根目录中
*/
public void gen(String sourceFile, String aimFile, Map<String, Object> data) {
try {
//创建Freemarker配置实例
Configuration conf = new Configuration();
//加载模板文件
Template template = conf.getTemplate(sourceFile);
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(aimFile), StandardCharsets.UTF_8));
template.process(data, out);
out.flush();
out.close();
} catch (IOException | TemplateException e) {
e.printStackTrace();
}
}
}
生成模板
<html>
<head>
<title>${title}</title>
</head>
<body>
<h2>${msg}</h2>
</body>
</html>
测试类
@SpringBootTest
public class GenTemplateTest {
@Resource
private GenUtil genUtil;
@Test
public void testGen() {
Map<String, Object> map = new HashMap<>();
map.put("title", "Crawling King Snake -- The Doors");
map.put("msg", "Get on out there on your hands and knees, baby");
genUtil.gen("src\\main\\resources\\templates\\genTemplate.ftl", "src/main/resources/templates/testGen.html", map);
}
}
结果
运行测试代码发现在当前项目的templates目录下生成了一个testGen.html的文件。如果你遇到这样的问题
Java HotSpot™ 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended