模板引擎
- 模板引擎的目标是“数据+模板=结果”
- 模板引擎将数据与展现有效的“解耦”
- 前端只需要知道怎么编写前端,后端只需关注后端,用模板引擎把两者整合
主流的模板引擎
-
Java Server Page(jsp)
-
FreeMarker
-
Beetl
(拥有前两者的优点,但是作为新的模板,还未普及,前面两个更常用)
FreeMarker和JSP
- 只要不是开发淘宝、京东这样的大型软件,两者的执行效率相差不多
FreeMarker
FreeMarker
是免费开源的模板引擎技术- 要下载
jar
包 FreeMarker
脚本为FreeMarker Template Language
FreeMarker
提供了大量内建函数来简化开发(这就是很多人放弃jsp用FreeMarker的原因)
在java类中创建数据
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import freemarker.core.ParseException;
import freemarker.template.Configuration;
import freemarker.template.MalformedTemplateNameException;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateNotFoundException;
public class FreeMarkerSample {
public static void main(String[] args) throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException, TemplateException {
//1、加载模板
//创建核心配置对象
/*传入版本号,每个版本的FreeMarker语法和解析方式都略有不同*/
Configuration config=new Configuration(Configuration.VERSION_2_3_28);
//设置加载的目录
/*第一个参数表示在这个类所在包中加载指定的ftl文件,第二个参数是空字符串,表示当前包 */
config.setClassForTemplateLoading(FreeMarkerSample.class, "");
//得到模板对象
/*因为上面指定了只在特定的类所在的包中加载ftl文件,所以这里直接写文件名就行*/
Template t=config.getTemplate("sample1.ftl");
//2、创建数据
/*FreeMarker中的数据就是一个Map对象5*/
Map<String,Object> data=new HashMap<>();
data.put("site", "百度");
data.put("url","http://www.baidu.com");
data.put("date",Calendar.getInstance().getTime());
data.put("number",456654456.7889);
Map<String,Object> info=new HashMap<>();
info.put("cpu","i5");
Computer computer=new Computer("123","张三",7896.5f,info);
data.put("compu",computer);
//3、产生输出(可以向控制台、网络中的流、文件中输出)
/*第一个参数是数据,第二个参数是向哪里输出*/
t.process(data,new OutputStreamWriter(System.out));//像控制台中输出
}
}
语法(均在.ftl文件中进行演示)
-
FTL
取值只有输出才要加
${}
<#-- FreeMarker取值 -->
${site}-${url}
<#-- 默认值,如果取的值不存在则会输出后面的默认值 -->
${author!"不存在的属性"}
<#-- 日期格式化输出,如果不格式化会抛异常(日期不能转成字符串) -->
${date?string("yyyy年MM月dd日 HH:mm:ss")}
<#-- 数字格式化输出,如果不指定格式,默认按国际化货币格式输出(三位一逗号,保留三位小数),四舍五入 -->
${number?string("0,0000.00")}
<#-- ${属性名.对象中的属性名} -->
ID:${compu.id}
User:${compu.user}
Price:${compu.price?string("0.00")}
<#-- 对象中集合属性中值的获取 -->
<#-- 数据中(Map中)保存对象的输出方式跟EL表达式差不多 -->
<#-- ${属性名.对象中集合属性的属性名["集合中的key"]}通过key获取value -->
CPU:${compu.info["cpu"]}
Memory:${compu.info["memory"]!"memory not exist"}
关于多级访问的变量,比如 animals.python.price
, 书写代码:animals.python.price!0
当且仅当 animals.python
永远存在, 而仅仅最后一个子变量 price
可能不存在时是正确的 (这种情况下我们假设价格是 0
)。 如果 animals
或 python
不存在, 那么模板处理过程将会以"未定义的变量"错误而停止。为了防止这种情况的发生, 可以如下这样来编写代码 (animals.python.price)!0
。 这种情况就是说 animals
或 python
不存在时, 表达式的结果是 0
。对于 ??
也是同样用来的处理这种逻辑的; 将 animals.python.price??
对比(animals.python.price)??
来看。
if
分支判断
<#-- if分支 -->
<#-- FreeMarker中字符串的判断直接用== -->
<#if compu.id=="123">
这是重要设配
<#elseif compu.id=="456">
正常保护等级
<#else>
接近废弃的电脑
</#if>
<#-- ??用于判断该对象是否为空,不为空返回true,为空返回false -->
<#if compu.user??>
这台电脑有人使用
<#else>
这台电脑无人使用
</#if>
switch
分支判断
<#--在FreeMarker中,空格也会输出,所以这里为了格式不留空格书写-->
<#-- switch分支 -->
<#switch compu.id>
<#case "123">
这台电脑很重要
<#break>
<#case "456">
正常保护等级
<#break>
<#default>
废弃电脑
</#switch>
list
迭代列表
<#-- 迭代List,并把值赋给arr,其中arr_index代表迭代的索引,从0开始 -->
<#list arrayList as arr>
${arr_index+1}-${arr}
</#list>
list
迭代Map
<#-- 把Map中的key取出来(linkedMap?keys)当作一个List,再迭代 -->
<#list linkedMap?keys as key>
${key}-${linkedMap[key]}
</#list>
FreeMarker内建函数
${words?replace("blood","*****")}
将字符串words
中的blood
子字符串替换为*****
输出
用?string
实现三目运算符的操作${(words?index_of("blood")!=-1)?String("包含敏感词汇",words)}
${name?cap_first}
${brand?upper_case}
${brand?length}
${words?replace("blood" , "*****")}
${words?index_of("blood")}
<#-- 利用?string实现三目运算符的操作 -->
${(words?index_of("blood") != -1)?string("包含敏感词汇","不包含敏感词汇")}
${n?round}
${n?floor}
${n?ceiling}
公司共有${computers?size}台电脑
第一台:${computers?first.model}
最后一台:${computers?last.model}
排序
<#-- 默认按照指定属性升序排列,可以调用reverse函数进行降序排列 -->
<#list computers?sort_by("price")?reverse as c>
${c.sn}-${c.price}
</#list>
Freemarker与Servlet的整合
ftl文件怎么在web项目中被执行
Freemarker
对整合servlet
有良好的支持,在FreeMarker.jar
包中存在支持Servlet的类
- 需要使用这个Servlet类就必须在
web.xml
中注册
<!--注册FreemarkerServlet -->
<servlet>
<servlet-name>freemarker</servlet-name>
<servlet-class>freemarker.ext.servlet.FreemarkerServlet</servlet-class>
<!--配置初始化参数,通知FreemarkerServlet在哪个目录下寻找.ftl文件(设置加载的目录) -->
<init-param>
<param-name>TemplatePath</param-name>
<param-value>/WEB-INF/ftl</param-value>
</init-param>
</servlet>
<!--FreemarkerServlet映射地址,只要浏览器地址栏中的URL是以.ftl结尾,就会被FreemarkerServlet处理 -->
<servlet-mapping>
<servlet-name>freemarker</servlet-name>
<url-pattern>*.ftl</url-pattern>
</servlet-mapping>
在配置初始化参数的时候,我们指定了FreemarkerServlet
在哪个目录下寻找.ftl
脚本文件,所以我们的.ftl
文件都要保存在这个文件中
通过在浏览器中输入http://localhost:8080/项目名/test.ftl
就可以输出这个脚本文件中的内容(并不要把完整的路径输入到浏览器中)
**执行原理:**当浏览器地址栏中输入的URL以.ftl
结尾,就会被FreemarkerServlet
类处理,FreemarkerServlet
类就会到web.xml
配置文件指定的加载目录中(就是init-param
中配置的templatePath
参数值)寻找和URL中同名的.ftl
脚本文件进行输出(上面是在/WEB-INF/ftl
文件下寻找test.ftl
文件进行输出)
Freemarker与servlet
ListServlet.java
@WebServlet("/list")
public class ListServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public ListServlet() {
super();
// TODO Auto-generated constructor stub
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Employee> employees=new ArrayList<>();
employees.add(new Employee(7823l,"李四","技术部","java高级工程师",15000f));
employees.add(new Employee(7824l,"张丹","市场部","客户经理",14500f));
request.setAttribute("employees", employees);
//FreemarkerServlet会检测到是以.ftl结尾的URL,进行处理(寻找对应的.ftl脚本文件进行输出)
request.getRequestDispatcher("/employee.ftl").forward(request, response);
}
}
employee.ftl
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>序号</th>
<th>员工编号</th>
<th>姓名</th>
<th>部门</th>
<th>职务</th>
<th>工资</th>
<th> </th>
</tr>
</thead>
<tbody>
<#--按员工编号升序输出-->
<#list employees?sort_by("empno") as emp>
<tr>
<td>${emp_index+1}</td>
<#-- 整数输出 -->
<td>${emp.empno?string("0")}</td>
<td>${emp.name}</td>
<td>${emp.department}</td>
<td>${emp.job}</td>
<td style="color: red;font-weight: bold">¥${emp.salary?string("0,000.00")}</td>
</tr>
</#list>
</tbody>
</table>
.ftl
脚本文件中的数据是从哪里获取的?
从当前页面的请求、会话、ServletContext
中自动依次查找,如果都没查找到,又没有做其他处理,则会抛出异常。