【问题
】当渲染文章数据时,由于文章的数据很多,直接去查询文章内容表的话,效率比较低。
【解决
】使用freemarker将文章的内容通过模板技术生成静态的html文件存储到minio中,这样用户就只需要拿着minio的url去minio里获取静态页面即可。效率大大提高
模板引擎
Freemarker是一种模板引擎:一种基于模板和要改变的数据,并用来生成输出文本(html网页、电子邮件、配置文件、源代码…)的通用工具。不是面向最终用户的,而是一个Java类库。
步骤
- 导入依赖
<dependencies>
<!-- freemarker -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
</dependencies>
- 添加application.yml配置
server:
port: 8881 #服务端口
spring:
application:
name: freemarker-demo #指定服务名
freemarker:
cache: false #关闭模板缓存,方便测试
settings:
template_update_delay: 0 #检查模板更新延迟时间,设置为0表示立即检查,如果时间大于0会有缓存不方便进行模板测试
suffix: .ftl #指定Freemarker模板文件的后缀名(默认是.ftlh)
suffix的属性也可以是.html、.xml、.jsp等,但是一般是以ftl作为扩展名
- 在resources下创建templates,此目录为
freemarker的默认模板存放目录
。在templates下创建模板文件basic.ftl
,模板中的插值表达式
最终会被freemarker替换成具体的数据。
basic.ftl文件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello World!</title>
</head>
<body>
<b>普通文本 String 展示:</b><br><br>
Hello ${name} <br>
<hr>
<b>对象Student中的数据展示:</b><br/>
姓名:${stu.name}<br/>
年龄:${stu.age}
<hr>
</body>
</html>
- 往模板里填数据,返回模板文件
@Controller // 因为要返回视图,而不是json字符串,所以这里不可以用@RestController
public class HelloController {
@GetMapping("/basic")
public String hello(Model model) {
// name
model.addAttribute("name", "xiaolin");
// stu
Student stu = new Student("03", 18);
model.addAttribute("stu", stu);
return "basic"; // 这里返回的数据必须和模板视图的名字一样,这样才能找到模板视图(别带后缀!!!)
}
}
Freemarker指令语法
基础语法种类
1. 注释<#-- -->
介于其之间的内容会被Freemarker忽视
<#--我是一个freemarker注释-->
2. 插值表达式${...}
Freemarker会用真实的值替代${…}
Hello ${name}
3. FTL指令<#> </#>
名字前加#
区分,Freemarker会解析标签中的表达式或逻辑。
例如:
<#list stus as stu>${stu.name}</#list>
4. 文本
仅文本信息,不是freemarker的注释、插值、FTL指令的内容会被Freemarker忽略解析,直接输出内容。
<#--freemarker中的普通文本-->
我是一个普通的文本
集合指令(List、Map)
List
<table>
<tr>
<td>序号</td>
<td>姓名</td>
<td>年龄</td>
<td>钱包</td>
</tr>
<#--遍历数据 stu指的是集合里的每一个数据-->
<#list stus as stu>
<tr>
<#--获取当前集合的下标-->
<td>${stu_index + 1}</td>
<td>${stu.name}</td>
<td>${stu.age}</td>
<td>${stu.money}</td>
</tr>
</#list>
</table>
Map
获取map中某个key对应的值
- 通过
map['keyname'].property
姓名:${stuMap['stu1'].name}<br/>
年龄:${stuMap['stu1'].age}<br/>
- 通过
map.keyname.property
姓名:${stuMap.stu2.name}<br/>
年龄:${stuMap.stu2.age}<br/>
遍历map
<table>
<tr>
<td>序号</td>
<td>姓名</td>
<td>年龄</td>
<td>钱包</td>
</tr>
<#--遍历数据 key指的是map中的每一个key-->
<#list stuMap?keys as key>
<tr>
<#--获取当前集合的下标-->
<td>${key_index + 1}</td>
<td>${stuMap[key].name}</td>
<td>${stuMap[key].age}</td>
<td>${stuMap[key].money}</td>
</tr>
</#list>
</table>
if指令
<#if 表达式>
<#else>
</if>
【例】:姓名为小红的数据字体显示为红色
<#if name = '小红'>
<span style = "color: red">${name}</span>
<#else>
<span>${name}</span>
</if>
在Freemarker中,
=
与==
是一样的
运算符
数值运算符
- 加法:+
- 减法:-
- 乘法:*
- 除法:/
- 求余:%
比较运算符
= 和 != 可以用于字符串、数值、日期来比较是否相等
= 和 != 两边必须是相同类型的值,否则会产生错误
字符串"x"和"x "和"X"比较是不相等的
gt
代替>
,因为Freemarker会把>解析成FTL标签结束的字符串,可以用括号避免这种情况,如:<#if (x > y)>
逻辑运算符
- 逻辑与:&&
- 逻辑或:||
- 逻辑非:!
空值处理
- 判断某个变量是否存在使用:“??”
用法:变量??
- 变量存在,返回true
- 不存在,返回false
<#if stus??>
<#list stus as stu>
...
<#list>
</#if>
- 缺失的变量使用:
!
用法:!默认值
- 使用
!
要指定一个默认值,当变量为空时显示默认值 - 如果是嵌套对象,要使用
()
括起来
- 使用
<#--如果name为空显示空字符串''-->
${name!''}
<#--如果stu或name为空,默认显示空字符串''-->
${(stu.name)!''}
内建函数
语法格式:变量 + ? + 函数名称
- 集合大小
${集合名?size}
- 日期格式化
显示日期+时间:${today?datetime}
自定义格式化:${today?string("yyyy年MM月")}
显示时分秒:${today?time}
显示年月日:${today?date}
- 内建函数c
【场景】:point是数值类型,使用${point}
会显示这个数字的数值,每三位使用逗号分隔。如果不想显示为每隔三位分割的数字,可以使用c函数将数字型转成字符串输出。
model.addAttribute("poin", 123456780L);
使用内建函数c:
${point?c}
- 将json字符串转为对象
<#--assign标签的作用:定义一个变量-->
<#assign text="{'bank':'工商银行','account':'10101920201920212'}" />
<#--把json转成变量-->
<#assign data=text?eval />
开户行:${data.bank}
账号:${data.account}
注意:eval的使用可能不安全,如果传入的字符串内容不可控(比如来自用户输入),可能会导致安全问题(例如代码注入)
输出静态化文件
@SpringBootTest
public class FreemarkerTest {
@Autowired
private Configuration configuration;
@Test
public void test() throws IOException, TemplateException {
Template template = configuration.getTemplate("test.ftl"); // 模板对象
/**
* 第一个参数:模型数据 mp
* 第二个参数:输出流
*/
Map<String, Object> mp = new HashMap<>();
mp.put("name", "xiaolin");
template.process(mp, new FileWriter("d:/list.html"));
}
}