模板引擎是为了能够更好的将前后端分离,将数据与界面有效解耦。现在较为常用的模板引擎包括JSP、Freemarker、Beetl本节主要介绍FreeMarker。FreeMarker使用FTL(FreeMarker Temple Language)脚本来获取后端的数据,其本身与JS类似是解释型脚本,所以执行效率较差,但开发效率和扩展性较高。
FreeMarker创建
先下载freemarker-2.3.23.jar包并将其导入项目中,并以此创建对象,获取模板对象并将数据输出至模板
package com.freemarker;
import freemarker.core.Configurable;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.Map;
public class Test {
public static void main(String[] args) throws IOException, TemplateException {
// 创建核心配置对象
Configuration configuration = new Configuration(Configuration.VERSION_2_3_23);
// 设置加载目录
configuration.setClassForTemplateLoading(Test.class, "");
// 获取模板对象
Template template = configuration.getTemplate("freemarker1.ftl");
// 创建对象
Map<String, Object> map = new HashMap<>();
map.put("description", "百度");
map.put("url", "http://www.baidu.com/");
// 向模板输出对象
template.process(map, new OutputStreamWriter(System.out));
}
}
创建FreeMarker脚本以ftl后缀名结尾,执行Java程序就可在控制台查看结果。
${description}-${url}
FTL数据获取
FreeMarker将数据都保存在Map集合中,在模板获取值时就可以按照key值将value值获取出来,例如:
-
${key}
:获取key所对应的值,若value值为JavaBean对象可以使用“.”加属性名来获取对象的值,若为Map集合可以使用“[key]”来获取值 -
${key!default}
:获取key所对应的值,若该值没有对应的值就获取default默认值 -
${key?string(...)}
:获取key所对应的值,并格式化
示例:
先实现JavaBean类
package com.freemarker.entity;
import java.util.Date;
import java.util.Map;
public class Computer {
private String sn; //序列号
private String model; //型号
private int state; //状态 1-在用 2-闲置 3-报废
private String user; //使用人
private Date dop; //采购日期
private Float price; //购买价格
private Map info; //电脑配置信息
...
// 省略构造方法和Get/Set方法
}
package com.freemarker;
import com.freemarker.entity.Computer;
import freemarker.core.Configurable;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.*;
public class Test {
public static void main(String[] args) throws IOException, TemplateException {
// 创建核心配置对象
Configuration configuration = new Configuration(Configuration.VERSION_2_3_23);
// 设置加载目录
configuration.setClassForTemplateLoading(Test.class, "");
// 获取模板对象
Template template = configuration.getTemplate("freemarker1.ftl");
// 创建对象
Map<String, Object> data = new HashMap<>();
Map info = new HashMap();
info.put("cpu", "i5");
Computer c1 = new Computer("1234567" , "ThinkPad" , 1 , "李四" , new Date() , 12900f , info);
data.put("computer", c1);
// 向模板输出对象
template.process(data, new OutputStreamWriter(System.out));
}
}
编写FTL文件,对日期格式和金额进行格式化,对于没有值的数据使用默认值
设备序列号:${computer.sn}
型号:${computer.model}
使用状态:${computer.state}
用户:${computer.user}
采购日期:${computer.dop?string("yyyy-MM-dd")}
价格:${computer.price?string("0.00")}
电脑配置信息
--------------------
CPU:${computer.info["cpu"]}
内存:${computer.memory!"暂无信息"}
条件判断
FreeMarker中内置了条件判断标签<#if>
和<#switch>
标签,用法上与Java语法类似,示例:
使用<#if>
标签判断电脑使用状态,修改FTL文件
设备序列号:${computer.sn}
型号:${computer.model}
使用状态:<#if computer.state == 1>在用<#elseif computer.state == 2>闲置<#elseif computer.state == 3>废弃</#if>
用户:${computer.user}
采购日期:${computer.dop?string("yyyy-MM-dd")}
价格:${computer.price?string("0.00")}
电脑配置信息
--------------------
CPU:${computer.info["cpu"]}
内存:${computer.memory!"暂无信息"}
使用<#switch>
标签
设备序列号:${computer.sn}
型号:${computer.model}
使用状态:<#switch computer.state><#case 1>在用<#break ><#case 2>闲置<#break ><#case 3>报废<#break ></#switch>
用户:${computer.user}
采购日期:${computer.dop?string("yyyy-MM-dd")}
价格:${computer.price?string("0.00")}
电脑配置信息
--------------------
CPU:${computer.info["cpu"]}
内存:${computer.memory!"暂无信息"}
在
<#if>
判断中使用<#if computer.user??>
标识是否存在,若存在则为true,不存在则为false
对于字符串的判断可以直接使用==
表示相等
循环迭代
FreeMarker中使用两种方法迭代输出集合,一种用于List集合,与Java语法中的循环迭代类似获取每个对象,使用${object_index}
表示循环到了第几次
<#list list as object>
${object_index} - ${object}
</#list>
另一种用于Map集合,获取集合中的key值
<#list Map?keys as key>
${key} - ${Map[keys]}
</#list>
示例:
JavaBean对象不变,增加数据到List集合和Map集合中
package com.freemarker;
import com.freemarker.entity.Computer;
import freemarker.core.Configurable;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.*;
public class Test {
public static void main(String[] args) throws IOException, TemplateException {
// 创建核心配置对象
Configuration configuration = new Configuration(Configuration.VERSION_2_3_23);
// 设置加载目录
configuration.setClassForTemplateLoading(Test.class, "");
// 获取模板对象
Template template = configuration.getTemplate("freemarker1.ftl");
// 创建对象
Map<String, Object> data = new HashMap<>();
List<Computer> computers = new ArrayList<>();
computers.add(new Computer("1234567" , "ThinkPad X1" , 2 , null , new Date() , 12999f , new HashMap() ));
computers.add(new Computer("1234568" , "HP XXX" , 1 , "张三" , new Date() , 7500f , new HashMap() ));
computers.add(new Computer("1234569" , "DELL XXX" , 3 , "李四" , new Date() , 8500f , new HashMap() ));
computers.add(new Computer("1234570" , "ACER XXX" , 1 , "王五" , new Date() , 6300f , new HashMap() ));
computers.add(new Computer("1234571" , "MSI XXX" , 1 , "赵六" , new Date() , 9300f , new HashMap() ));
//LinkedHashMap可以保证数据按存放顺序进行提取
Map<String, Computer> map = new LinkedHashMap<>();
for(Computer c : computers) {
map.put(c.getSn(), c);
}
data.put("computers", computers);
data.put("computer_map", map);
// 向模板输出对象
template.process(data, new OutputStreamWriter(System.out));
}
}
修改FTL模板
<#list computers as c>
序号:${c_index + 1} <#-- 迭代变量_index保存了循环的索引,从0开始 -->
SN:${c.sn}
型号:${c.model}
<#switch c.state>
<#case 1>
状态:使用中
<#break>
<#case 2>
状态:闲置
<#break>
<#case 3>
状态:已作废
<#break>
</#switch>
<#if c.user??>
用户:${c.user}
</#if>
采购日期:${c.dop?string("yyyy-MM-dd")}
采购价格:${c.price?string("0.00")}
-------------------------------------------
</#list>
==========================================
<#list computer_map?keys as k >
${k}-${computer_map[k].model}
${computer_map[k].price?string("0.00")}
</#list>
FreeMarker与Servlet的结合开发
freemarker的jar包中包含了对Servlet的支持,所以在使用是可以在web.xml中直接配置该Servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>freemarker</servlet-name>
<servlet-class>freemarker.ext.servlet.FreemarkerServlet</servlet-class>
<init-param>
<param-name>TemplatePath</param-name>
<param-value>/WEB-INF/ftl</param-value>
</init-param>
<init-param>
<param-name>DefaultEncoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>freemarker</servlet-name>
<url-pattern>*.ftl</url-pattern>
</servlet-mapping>
</web-app>
TemplatePath是指向FTL模板的路径,DefaultEncoding描述模板的编码格式这里一定要指定否则中文会乱码。映射地址使用模糊匹配所有的FTL文件。
package com.freemarker.servlet;
import com.freemarker.entity.Employee;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@WebServlet(urlPatterns = "/list")
public class EmployeeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 创建实体类并将其放在集合中
List<Employee> list = new ArrayList<>();
list.add(new Employee(7731,"张三" , "市场部" , "客户代表" , 8000f));
list.add(new Employee(7731,"张三" , "市场部" , "客户代表" , 8000f));
// 将list属性放到Request属性中
req.setAttribute("employees", list);
// 将请求转发给FTL
req.getRequestDispatcher("/employee.ftl").forward(req, resp);
}
}
在访问该Servlet时就会将请求转发给freemarker服务去处理。
FreeMarkerServlet会对Web应用对象根据作用范围依次检索属性,若请求中没有属性会去检索用户会话,若会话中没有会去检索全局对象,所以可以将要返回的信息放在任意一个对象中。