五、FreeMarker
5.1 FreeMarker介绍
FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
模板编写为FreeMarker Template Language (FTL)。它是简单的,专用的语言, 不是 像PHP那样成熟的编程语言。 那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据。
这种方式通常被称为 MVC (模型 视图 控制器) 模式,对于动态网页来说,是一种特别流行的模式。 它帮助从开发人员(Java 程序员)中分离出网页设计师(HTML设计师)。设计师无需面对模板中的复杂逻辑, 在没有程序员来修改或重新编译代码时,也可以修改页面的样式。
而FreeMarker最初的设计,是被用来在MVC模式的Web开发框架中生成HTML页面的,它没有被绑定到 Servlet或HTML或任意Web相关的东西上。它也可以用于非Web应用环境中。
5.2 FreeMarker的特性
5.2.1 通用目标
能够生成各种文本:HTML、XML、RTF、Java 源代码等等
易于嵌入到你的产品中:轻量级;不需要 Servlet 环境
插件式模板载入器:可以从任何源载入模板,如本地文件、数据库等等
你可以按你所需生成文本:保存到本地文件;作为 Email 发送;从 Web 应用程序发送它返回给 Web浏览器
5.2.2强大的模板语言
所有常用的指令:include、if/elseif/else、循环结构
在模板中创建和改变变量
几乎在任何地方都可以使用复杂表达式来指定值
命名的宏,可以具有位置参数和嵌套内容
名字空间有助于建立和维护可重用的宏库,或将大工程分成模块,而不必担心名字冲突
输出转换块:在嵌套模板片段生成输出时,转换HTML转义、压缩、语法高亮等等;你可以定义自己
的转换
5.2.3通用数据模型
FreeMarker不是直接反射到Java对象,Java对象通过插件式对象封装,以变量方式在模板中显示
你可以使用抽象(接口)方式表示对象(JavaBean、XML文档、SQL查询结果集等等),告诉模板开 发者使用方法,使其不受技术细节的打扰
5.2.4 为Web准备
在模板语言中内建处理典型Web相关任务(如HTML转义)的结构
能够集成到Model2 Web应用框架中作为JSP的替代
支持JSP标记库
为MVC模式设计:分离可视化设计和应用程序逻辑;分离页面设计员和程序员
5.2.5智能的国际化和本地化
字符集智能化(内部使用UNICODE)
数字格式本地化敏感
日期和时间格式本地化敏感
非US字符集可以用作标识(如变量名)
多种不同语言的相同模板
5.2.6强大的XML处理能力
<#recurse> 和 <#visit> 指令(2.3版本)用于递归遍历XML树。在模板中清楚和直接的访问XML对象
模型。开源论坛 JForum 就是使用了 FreeMarker 做为页面模板。
5.3 FreeMarker环境搭建步骤
- 新建一个Maven Web项目
- 配置坐标依赖和部署插件
- 在项目的webapp/WEB-INF目录下的web.xml文件中,添加freemarker 相关 servlet 配置
<!-- freemarker的坐标依赖 -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
<!-- servlet-api的坐标依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>- 修改配置文件web.xml
- 在项目的webapp/WEB-INF目录下的web.xml文件中,添加freemarker 相关 servlet 配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <!-- FreeMarker 的Servlet配置 -->
<servlet>
<servlet-name>freemarker</servlet-name>
<servlet-class>freemarker.ext.servlet.FreemarkerServlet</servlet-class>
<init-param> <!-- 模板路径 -->
<param-name>TemplatePath</param-name> <!-- 默认在webapp目录下查找对应的模板文件 -->
<param-value>/</param-value>
</init-param>
<init-param> <!-- 模板默认的编码:UTF-8 -->
<param-name>default_encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<!-- 2.3.4. 编写Servlet类 2.3.5. 新建模板文件 ftl-->
<!-- 在webapp目录下新建template文件夹,创建f01.ftl文件-->
<!-- 2.3.6. 启动项目-->
</servlet> <!-- 处理所有以.ftl结尾的文件;ftl是freemarker默认的文件后缀 -->
<servlet-mapping>
<servlet-name>freemarker</servlet-name>
<url-pattern>*.ftl</url-pattern>
</servlet-mapping>
</web-app>- 编写Servlet 类
@WebServlet("/f1")
public class FreeMarker extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//创建域对象属性
req.setAttribute("mgr","hello");
//跳转到页面中
req.getRequestDispatcher("TemplatePath/freemarker01.ftl").forward(req,resp);
}
}- 新建模板文件 ftl
- 在webapp目录下新建template文件夹,创建freemarker01.ftl文件
<#--ftl文件内容-->
${mgr}- 启动项目
- 访问项目
5.4 FreeMarker 的数据类型
- 布尔型:
- 等价于 Java 的 Boolean 类型,不同的是不能直接输出,可转换为字符串输出
- 日期型:
- 等价于 java 的 Date 类型,不同的是不能直接输出,需要转换成字符串再输出
- 数值型:
- 等价于 java 中的 int,float,double 等数值类型
- 三种显示形式:数值型(默认)、货币型、百分比型
- 字符型:
- 等价于 java 中的字符串,有很多内置函数
- sequence 类型:
- 等价于 java 中的数组,list,set 等集合类型
- hash 类型:
- 等价于 java 中的 Map 类型
5.4.1 布尔类型
boolean类型的数据是不能直接输出,需要转换成字符串使用
5.4.2 日期类型
在freemarker中日期类型不能直接输出;如果输出要先转成日期型或字符串
- 转换规则:
1. 年月日 ?date
2. 时分秒 ?time
3. 年月日时分秒 ?datetime
4. 指定格式 ?string("自定义格式")
y:年 M:月 d:日
H:时 m:分 s:秒- 示例:
// 在servlet中设置日期类型
request.setAttribute("createDate",new Date());<#-- 在.ftl文件中获取数据 -->
<#-- 输出日期格式 -->
${createDate?date} <br>
<#-- 输出时间格式 -->
${createDate?time} <br>
<#-- 输出日期时间格式 -->
${createDate?datetime} <br>
<#-- 输出格式化日期格式 -->
${createDate?string("yyyy年MM月dd日 HH:mm:ss")} <br>5.4.3 数值类型
数值类型在freemarker中数值类型可以直接输出;
- 转换规则:
1. 转字符串
普通字符串 ?c
货币型字符串 ?string.currency
百分比型字符串 ?string.percent
2. 保留浮点型数值指定小数位(#表示一个小数位)
?string["0.##"]- 示例:
// 在servlet中设置数值类型
req.setAttribute("age",18); // 数值型
req.setAttribute("salary",10000); // 数值型
req.setAttribute("avg",0.545); // 浮点型<#-- 在.ftl文件中获取数据 -->
<#-- 直接输出数值型 -->
${age} <br> ${salary} <br>
<#-- 将数值转换成字符串输出 -->
${salary?c} <br>
<#-- 将数值转换成货币类型的字符串输出 -->
${salary?string.currency} <br>
<#-- 将数值转换成百分比类型的字符串输出 -->
${avg?string.percent} <br>
<#-- 将浮点型数值保留指定小数位输出 (##表示保留两位小数) -->
${avg?string["0.##"]} <br>5.4.4 字符串类型
- 转换规则:
直接输出,常用方法如下
在freemarker中字符串类型可以直接输出;
1. 截取字符串(左闭右开) ?substring(start,end)
2. 首字母小写输出 ?uncap_first
3. 首字母大写输出 ?cap_first
4. 字母转小写输出 ?lower_case
5. 字母转大写输出 ?upper_case
6. 获取字符串长度 ?length
7. 是否以指定字符开头(boolean类型) ?starts_with("xx")?string
8. 是否以指定字符结尾(boolean类型) ?ends_with("xx")?string
9. 获取指定字符的索引 ?index_of("xx")
10. 去除字符串前后空格 ?trim
11. 替换指定字符串 ?replace("xx","xx")- 示例:
// 在servlet中设置数值类型
request.setAttribute("msg","Hello ");
request.setAttribute("msg2","freemarker");<#-- 在.ftl文件中获取数据 -->
<#-- 直接输出 -->
${msg} - ${msg2} <br>
<#--1. 截取字符串(左闭右开) ?substring(start,end)-->
${msg?substring(0,2)}<br>
<#--2. 首字母小写输出 ?uncap_first-->
${msg?uncap_first}<br>
<#--3. 首字母大写输出 ?cap_first-->
${msg2?cap_first}<br>
<#--4. 字母转小写输出 ?lower_case-->
${msg?lower_case}<br>
<#--5. 字母转大写输出 ?upper_case-->
${msg2?upper_case}<br>
<#--6. 获取字符串长度 ?length-->
${msg2?length}<br>
<#--7. 是否以指定字符开头(boolean类型) ?starts_with("xx")?string-->
${msg?starts_with("H")?c}<br>
<#--8. 是否以指定字符结尾(boolean类型) ?ends_with("xx")?string-->
${msg2?ends_with("e")?c}<br>
<#--9. 获取指定字符的索引 ?index_of("xx")-->
${msg2?index_of("r")}<br>
<#--10. 去除字符串前后空格 ?trim-->
${msg2?trim}<br>
<#--11. 替换指定字符串 ?replace("xx","xx")-->
${msg2?replace("ker","abc")}<br>5.4.5 sequence类型
在Servlet中设置序列类型(数组、List、Set)的数据
- List格式:
<#list 序列名 as 元素名>
${名称}
</#list>- 序列类型常用方法
获取序列的长度 ${序列名?size}
获取序列元素的下标 ${元素名?index}
获取第一个元素 ${序列名?first}
获取最后一个元素 ${序列名?last}
倒序输出 序列名?reverse
升序输出 序列名?sort
降序输出 序列名?sort?reverse
指定字段名排序 序列名?sort_by("字段名")- 示例:
// 在servlet中设置数值类型
// 数组操作
String[] stars = new String[]{"周杰伦","林俊杰","陈奕迅","五月天"}; request.setAttribute("stars",stars);
// List操作
List<String> citys = Arrays.asList("上海","北京","杭州","深圳");
request.setAttribute("cityList",citys);
// JavaBean集合
List<User> userList = new ArrayList<>();
userList.add(new User(1,"zhangsan",22));
userList.add(new User(2,"lisi",18));
userList.add(new User(3,"wangwu",20));
request.setAttribute("userList",userList);<#-- 数组操作 -->
<#list stars as star>
下标:${star?index} -- 名字:${star} <br>
</#list>
数组的长度:${stars?size} <br>
<#-- 获取第一个元素 -->
第一个元素:${stars?first} <br>
<#-- 获取最后一个元素 -->
最后一个元素:${stars?last} <br>
<hr>
<#-- List操作 -->
<#list cityList as city > ${city} <br>
</#list>
List的size:${cityList?size} <br>
<#-- 倒序输出 -->
<#list cityList?reverse as city >
${city} - </#list> <br>
<#-- 升序输出 -->
<#list cityList?sort as city >
${city} -
</#list> <br>
<#-- 降序输出 -->
<#list cityList?sort?reverse as city >5.4.6 hash 类型
- 格式:
key遍历输出
<#list hash?keys as key>
${key} -- ${hash[key]}
</#list>
value遍历输出
<#list hash?values as value>
${value}
</#list>- 示例:
// Map操作
Map<String,String> cityMap = new HashMap<>();
cityMap.put("sh","上海");
cityMap.put("bj","北京");
cityMap.put("sz","深圳");
request.setAttribute("cityMap",cityMap);<#-- key遍历输出 -->
<#list cityMap?keys as key>
${key} -- ${cityMap[key]} <br>
</#list>
<#-- value遍历输出 -->
<#list cityMap?values as value>
${value}
</#list>5.5 FreeMarker 常用指令
5.5.1自定义变量指令
使用 assign 指令你可以创建一个新的变量, 或者替换一个已经存在的变量。
- 语法:
- <#assign 变量名=值>
- <#assign 变量名=值 变量名=值> (定义多个变量)
- 示例
<#-- 直接在.ftl文件中定义后直接运行输出-->
<#assign str="hello">
${str} <br>
<#assign num=1 names=["zhangsan","lisi","wangwu"] >
${num} -- ${names?join(",")}5.5.2 if elseif else 逻辑判断指令
可以使用 if , elseif 和 else 指令来条件判断是否满足某些条件。
- 格式:
<#if condition>
...
<#elseif condition2>
...
<#else>
...
</#if>- 注意
- condition, condition2等:将被计算成布尔值的表达式。
- elseif 和 else 指令 是可选的。
- elseif中的比较运算符 > 改成 gt <改成 lt
- 示例:
<#assign score =99>
<#if score<60 >
不及格
<#elseif score gt 60 && score lt 80 >
及格
<#else>
优秀
</#if>5.5.3 list遍历指令
- 格式1:
<#list sequence as item>
</#list> - 格式2:
<#list sequence as item>
<#else>
当没有选项时,执行else指令
</#list> - 注意:
- else 部分是可选的
- sequence: 想要迭代的项,可以是序列或集合的表达式
- item: 循环变量 的名称
- 当没有迭代项时,才使用 else 指令, 可以输出一些特殊的内容而不只是空在那里
- 示例:
<#assign users = ["张三","李四","王五"]>
<#-- 遍历序列 -->
<#list users as user>
${user}
</#list>5.5.4 macro 自定义指令 宏
自定义指令需要先定义,再使用(用@来使用)
- 基本使用定义:
- 定义格式:
<#macro 指令名>
指令内容
</#macro> - 使用:
<@指令名>< /@指令名>
- 带有参数的自定义指令
- 格式:
<#macro 指令名 参数名1 参数名2>
指令内容
</#macro> - 使用:
<@指令名 参数名1=参数值1 参数名2=参数值2>< /@指令名 >
- 注意:
- 指令可以被多次使用。
- 自定义指令中可以包含字符串,也可包含内置指令
- 示例:
<#--宏 自定义指令-->
<#macro test>
<h4>test指令</h4>
</#macro>
<#--宏 自定义指令 一个参数的宏定义方法-->
<#macro printCFB num>
<#list 1..num as i>
<#list 1..i as j>
${j} * ${i} = ${i*j}
</#list>
<br>
</#list>
</#macro>
<#--宏 自定义指令 多参数-->
<#macro pr num1 num2>
${num1}--${num2}
</#macro>
<#--宏 自定义指令的使用-->
<@test></@test>
<@printCFB 5></@printCFB>
<@pr num1=1 num2=3></@pr>5.5.5 nested占位指令
nested 相当于占位符,一般结合macro指令一起使用。
可以将自定义指令中的内容通过nested指令占位,当使用自定义指令时,会将占位内容显示。
<#macro test>
这是一段文本!
<#nested>
<#nested>
</#macro>
<#--使用test宏-->
<@test>
<h4>这是文本后面的内容!</h4>
</@test>5.5.6 import导入指令
import 指令可以引入一个库。也就是说,它创建一个新的命名空间, 然后在那个命名空间中执行给定路径的模板。可以使用引入的空间中的指令。
在其他ftl页面中通过import导入你想要页面的.ftl的模板,然后就可以使用该命名空间中的指令
- 示例:
<#--import导入-->
<#import "freemarker03.ftl" as zl>
<@zl.printCFB 3></@zl.printCFB>通过导入freemarker03.ftl的路径,就可以使用该路径下的模板内容以及自定义的宏的方法
5.5.7 include包含标签
可以使用 include 指令在你的模板中插入另外一个 FreeMarker 模板文件 。 被包含模板的输出格式是在 include 标签出现的位置插入的。 被包含的文件和包含它的模板共享变量,就像是被复制粘贴进去的一样。
- 示例:
<#--包含指令(引入其他页面文件) include-->
<#--html文件-->
<#include "test.html">
<#--freemarker文件-->
<#include "test.ftl">
<#--text文件-->
<#include "test.txt">
















