模板引擎:FreeMarker
FreeMarker 是一种模板引擎,主要用于生成文本输出,如网页、电子邮件、配置文件等。
简介
主要特点
- 强大的模板语言
- 支持变量、表达式、循环、条件判断等基本编程结构,能够灵活地处理数据并生成动态内容。
- 可以方便地访问 Java 对象的属性和方法,实现数据的展示和处理。
- 与 Java 集成良好
- 可以在 Java 应用程序中轻松地使用 FreeMarker 来生成动态的 HTML、XML 或其他文本格式的输出。
- 支持与各种 Java 框架集成,如 Spring、Struts 等。
- 多用途
- 不仅适用于 Web 开发,还可以用于生成报告、文档、代码模板等多种场景。
- 可以根据不同的需求定制模板,提高开发效率。
工作原理
- 模板文件
- 由 FreeMarker 模板语言编写的文件,包含静态内容和动态指令。
- 静态内容在生成输出时直接复制到输出中,动态指令则根据数据模型进行处理并生成相应的输出内容。
- 数据模型
- 通常是一个 Java 对象,包含要在模板中显示的数据。
- 可以通过配置或编程的方式将数据模型传递给 FreeMarker 进行处理。
- 模板引擎
- 负责解析模板文件和数据模型,并生成最终的输出文本。
- 根据模板中的指令,从数据模型中获取数据并进行相应的处理,将结果插入到输出中。
使用步骤
- 引入 FreeMarker 库 在 Java 项目中,需要添加 FreeMarker 的依赖库。
- 创建模板文件
- 使用FreeMarker 模板语言编写模板文件,定义静态内容和动态指令。
- 准备数据模型 创建一个 Java 对象,包含要在模板中显示的数据。
- 配置 FreeMarker 设置模板文件的路径、编码等参数。
- 生成输出
- 使用 FreeMarker 的API,将数据模型传递给模板引擎,生成最终的输出文本。
总之,FreeMarker 是一个功能强大的模板引擎,能够帮助开发人员快速生成动态的文本输出,提高开发效率。它在 Java 开发中得到了广泛的应用,特别是在 Web 开发领域。
环境搭建&快速入门
导入依赖
<!-- web的起步依赖 -->
<!-- 后端项目一般导入,应为要使用controller功能 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- freemarker的起步依赖 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- lombok的依赖,提供一些注解来简化开发 -->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency>
<!-- apahce 对 java io 的封装,用来操作一些文件 -->
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-io</artifactId><version>1.3.2</version>
</dependency>
freemarker配置
spring:freemarker:# 是否开启Freemarker缓存,本地开发环境建议设置为false,生产环境建议设置为true(生产环境关闭,方便测试)cache: false# 设置字符集charset: UTF-8# 禁止使用请求覆盖模型allow-request-override: false# 检查模板位置是否存在check-template-location: true# 内容类型content-type: text/html# 是否暴露请求属性expose-request-attributes: true# 是否暴露会话属性expose-session-attributes: true# 文件后缀suffix: .ftl# 模板加载路径(在导出为html文件的时候有作用)template-loader-path: classpath:/templates/
创建freemarker的模板存放目录
在resource
下创建templates
目录,此目录为默认的freemarker的模板存放目录。
在templates
下创建模板文件01-basic.ftl
,模板中的插值表达式最终会被freemarker替换成具体的数据。
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>Hello Worldl</title>
</head>
<body>
<b>普通文本 String 展示:</b><br><br> Hello ${name} <br>
<hr>
<b>对象 Student 中的数据展示: </b><br/>姓名: ${stu.name}<br/>年龄: ${stu.age}
<hr>
</body>
</html>
其中的
${name}``${stu.name}``${stu.age}
是freemarker的插值表达式,插值表达式最终会被替换为具体的数据。freemarker作为springmvc-一种视图格式,默认情况下SpringMVC支持freemarkeri视图格式。
controller中使用模板
编写一个controller:
import com.shiguang.authtest.entity.Student;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;import java.util.Date;@Controller
@RequestMapping("freemarker")
public class TestFreeMarkerController {@RequestMapping("basic")public String test(Model model){model.addAttribute("name", "freemarker");Student student = new Student();student.setAge(18);student.setName("张三");student.setBirthday(new Date());student.setMoney(1000.86);model.addAttribute("stu", student);return "01-basic";}
}
import lombok.Data;import java.util.Date;@Data
public class Student{private String name;private int age;private Date birthday;private double money;
}
启动项目访问该页面,发现数据绑定到了html上。
注意:
- 模板中的插值表达式
${stu.name}
和model.addAttribute("stu", student)
对应,他会读取到该对象下的name属性。Model
是org.springframework.ui.Model
包下的。- 类上不能使用
@RestController注解
,因为它返回的是html模板,应该使用@Controller
。
指令语法
注释
注释,即<#-- -->
,介于之间的内容会被忽略。
<#-- 这是注释的内容 -->
插值
即为${}
,介于之间的内容会被真实数据替代。
<a>hello ${name}</a>
FTL指令(重点)
即为<#>FTL指令</#>
,和HTML标记类似,名字前加#予以区分,FreeMarker会解析标签中的表达式或逻辑。
FTL指令(List)
controller类中的写法:
import com.shiguang.authtest.entity.Student;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;import java.util.Date;@Controller
@RequestMapping("freemarker")
public class TestFreeMarkerController {@RequestMapping("basic")public String test(Model model){model.addAttribute("name", "freemarker");Student student1 = new Student();student1.setAge(18);student1.setName("张三");student1.setBirthday(new Date());student.setMoney(1000.86);Student student2 = new Student();student2.setAge(21);student2.setName("李四");student2.setBirthday(new Date());student.setMoney(2009.86);List<Student> students = new ArrayList<>();students.add(student1);students.add(student2);model.addAttribute("stus", students);return "01-basic";}
}
模板中的使用:
<#-- 在controller中Model中添加的是Student的集合(ArryList) -->
<#-- stus是controller中在model中添加的集合数据的key,stu代指集合中被遍历的别名 -->
<#list stus as stu><tr><#-- 使用 别名_index 可以拿到对应元素的下标 --><td>${stu_index}</td><#-- 下标加一可以拿到当前元素的序号 --><td>${stu_index + 1}</td><td>${stu.name}</td><td>${stu.age}</td><td>${stu.money}</td></tr>
</#list>
FTL指令(Map)
controller类中的写法:
import com.shiguang.authtest.entity.Student;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;import java.util.Date;@Controller
@RequestMapping("freemarker")
public class TestFreeMarkerController {@RequestMapping("basic")public String test(Model model){model.addAttribute("name", "freemarker");Student student1 = new Student();student1.setAge(18);student1.setName("张三");student1.setBirthday(new Date());student.setMoney(1000.86);Student student2 = new Student();student2.setAge(21);student2.setName("李四");student2.setBirthday(new Date());student.setMoney(2009.86);Map<String,Student> students = new HashMap<>();stus.put("zc",student1);stus.put("ls",student2);model.addAttribute("stus", students);return "01-basic";}
}
模板中的使用:
<#-- 方式一:可以通过${Model中键的名称['键的名称'].值的属性名}获取map值 -->
<a>${stus['zc'].name}</a>
<#-- 方式二:可以通过${Model中键的名称.键的名称.值的属性名}获取map值 -->
<a>${stus.zc.name}</a>
<#-- map的遍历:map的遍历需要使用list,获取到map中所有key的名称,然后再通过key来访问map -->
<#list stus?keys as key><#-- 使用 别名_index 可以拿到对应元素的下标 --><td>${key_index}</td><#-- 下标加一可以拿到当前元素的序号 --><td>${key_index + 1}</td><td>${stus[key].name}</td><td>${stus[key].age}</td><td>${stus[key].money}</td>
</#list>
FTL指令(If)
controller类中的写法:
import com.shiguang.authtest.entity.Student;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;import java.util.Date;@Controller
@RequestMapping("freemarker")
public class TestFreeMarkerController {@RequestMapping("basic")public String test(Model model){model.addAttribute("name", "freemarker");Student student1 = new Student();student1.setAge(18);student1.setName("张三");student1.setBirthday(new Date());student.setMoney(1000.86);Student student2 = new Student();student2.setAge(21);student2.setName("李四");student2.setBirthday(new Date());student.setMoney(2009.86);List<Student> students = new ArrayList<>();students.add(student1);students.add(student2);model.addAttribute("stus", students);return "01-basic";}
}
模板中的使用:
<#-- if语句基本格式 -->
<#if 判断条件表达式>
<#else>
</#if><#-- 用法示例:如果是“张三”则修改为红色 -->
<#list stus as stu><tr><#if stu.name='张三'><td style="color:red;">${stu.name}</td><#else><td>${stu.name}</td></#if><td>${stu.age}</td><td>${stu.money}</td></tr>
</#list>
注意:
在freemarker中,判断是否相等表达式中,=与==是一样。
FTL指令(数学表达式)
FreeMarker表达式中完全支持算数运算,FreeMarker支持的运算符包括:
算数运算符 | 说明 |
---|---|
加载 | + |
减法 | - |
乘法 | * |
除法 | / |
求模(求余) | % |
比较运算符 | 说明 |
---|---|
=或者== | 判断两个值是否相等 |
!= | 判断两个值是否不等 |
>或者gt | 判断左边值是否大于右边值 |
>=或者gte | 判断左边值是否大于等于右边值 |
<或者lt | 判断左边值是否小于右边值 |
<=或者lte | 判断左边值是否小于等于右边值 |
:::danger
注意:
- =和!=可以用于字符串、数值和日期来比较是否相等
- =和!=两边必须是相同类型的值,否则会产生错误
- 字符串"x",“X”,"×"的比较是不相等的,因为freemarker是精确匹配
- gt代替>,freemarker会把>解释成FTL标签的结束字符,可使用括号避免这种情况,如<#if(x>y)>
:::
运算符 | 说明 |
---|---|
&& | 逻辑与 |
! | 逻辑非 |
空值处理
变量是否存在
freemarker中如果使用的变量不存在,则会报错。
判断某个变量是否存在使用"??",用法为variable??
,如果该变量存在则返回true
,否则返回false
我们可以在使用变量的时候,进行存在判断,保证不会报错,例如:
<#if stus??><#-- 详细的对stus的操作 -->
</#>
变量缺失
如果想要给定一个不存在的变量默认值,则可以使用"!",用法为${name!'这是默认值'}
,如果name变量不存在,这个插值表达式就会返回"这是默认值"这个字符串。
如果是嵌套对象则建议使用()
括起来,例如:${(stu.name)!'这是默认值'}
。
内建函数
内建函数语法格式:变量?函数名称
,之前的map名称?keys
就是内建函数。
内建函数 | 说明 |
---|---|
集合名称?size | 获取集合的大小 |
map名称?keys | 获取map的key的集合 |
日期类型变量?date | 显示年月日 |
日期类型变量?time | 显示时分秒 |
日期类型变量?datetime | 显示日期+时间 |
日期类型变量?string("yyyy年MM月") | 定义格式化 |
数字类型变量?c | 将数字类型转换为字符串输出(默认情况下的数字类型会每三位之间一个逗号分隔符) |
JSON字符串类型?eval | 将JSON字符串转换为对象类型(转化为对象类型后可以使用插值表达式获取值) |
普通文本
文本,仅文本信息,这些不是freemarker的注释、插值、FTL指令的内容会被freemarker忽略解析,直接输出内容。
这是一个普通文本
输出静态化文件
使用freemarker将原生的API界面生成html文件(通过模板+数据)–>生成html界面
我们可以通过使用reemarker.template
包下的Configuration
,来实现将freemarker模板输出为html页面。
java代码中怎么写:
import freemarker.template.Configuration;public class FreeMarkerTest{// 注意,这里注入的Configuration是哪个包下的@Autowiredprivate Configuration configuration;public void soutFreeMarkerToTtml(){// 指定模板Template template = configuration.getTemplate("01-basic.ftl");//需要在配置文件中的template-loader-path指定加载路径,否则会找不到模板/*** 参数说明* 参数1:模型数据(Map集合)* 参数2:输出流*/template.process(getData(),new FileWriter("d:/test/list.html")); }public Map getData(){Map<String,Object> templateData = new HashMap<>();templateData.put("name", "freemarker");Student student1 = new Student();student1.setAge(18);student1.setName("张三");student1.setBirthday(new Date());student.setMoney(1000.86);Student student2 = new Student();student2.setAge(21);student2.setName("李四");student2.setBirthday(new Date());student.setMoney(2009.86);List<Student> students = new ArrayList<>();students.add(student1);students.add(student2);templateData.put("stus", students);}
}