问题】当渲染文章数据时,由于文章的数据很多,直接去查询文章内容表的话,效率比较低。
解决】使用freemarker将文章的内容通过模板技术生成静态的html文件存储到minio中,这样用户就只需要拿着minio的url去minio里获取静态页面即可。效率大大提高
369f91bc04ab4b13a3024b7cda9063c5.png

模板引擎

Freemarker是一种模板引擎:一种基于模板和要改变的数据,并用来生成输出文本(html网页、电子邮件、配置文件、源代码…)的通用工具。不是面向最终用户的,而是一个Java类库。
bb81ce762b244cc693aeb5119f053c43.png

步骤

  1. 导入依赖
<dependencies>
<!-- freemarker -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
</dependencies>
  1. 添加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作为扩展名

  1. 在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>
  1. 往模板里填数据,返回模板文件
@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对应的值

  1. 通过map['keyname'].property
姓名:${stuMap['stu1'].name}<br/>
年龄:${stuMap['stu1'].age}<br/>
  1. 通过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中,=== 是一样的

运算符

数值运算符

  1. 加法:+
  2. 减法:-
  3. 乘法:*
  4. 除法:/
  5. 求余:%

比较运算符

846b7c2736084785a2100f9429e9c333.png

= 和 != 可以用于字符串、数值、日期来比较是否相等
= 和 != 两边必须是相同类型的值,否则会产生错误
字符串”x”和”x “和”X”比较是不相等的
gt代替>,因为Freemarker会把>解析成FTL标签结束的字符串,可以用括号避免这种情况,如:<#if (x > y)>

逻辑运算符

  1. 逻辑与:&&
  2. 逻辑或:||
  3. 逻辑非:!

空值处理

  1. 判断某个变量是否存在使用:”??”
    用法:变量??
    • 变量存在,返回true
    • 不存在,返回false
<#if stus??>
<#list stus as stu>
...
<#list>
</#if>
  1. 缺失的变量使用:!
    用法:!默认值
    • 使用!要指定一个默认值,当变量为空时显示默认值
    • 如果是嵌套对象,要使用()括起来
<#--如果name为空显示空字符串''-->
${name!''}
<#--如果stu或name为空,默认显示空字符串''-->
${(stu.name)!''}

内建函数

语法格式:变量 + ? + 函数名称

  1. 集合大小
${集合名?size}
  1. 日期格式化
    显示日期+时间:${today?datetime}
    自定义格式化:${today?string("yyyy年MM月")}
    显示时分秒:${today?time}
    显示年月日:${today?date}
  2. 内建函数c
    【场景】:point是数值类型,使用${point}会显示这个数字的数值,每三位使用逗号分隔。如果不想显示为每隔三位分割的数字,可以使用c函数将数字型转成字符串输出。
    62e4f1978b4447a3b40fd0c46c45e969.png
model.addAttribute("poin", 123456780L);

使用内建函数c:

${point?c}
  1. 将json字符串转为对象
<#--assign标签的作用:定义一个变量-->
<#assign text="{'bank':'工商银行','account':'10101920201920212'}" />
<#--把json转成变量-->
<#assign data=text?eval />
开户行:${data.bank}
账号:${data.account}

注意:eval的使用可能不安全,如果传入的字符串内容不可控(比如来自用户输入),可能会导致安全问题(例如代码注入)

输出静态化文件

f2aa177960304cbeab1740b830d65999.png

@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"));
}
}