• 了解模版引擎原理
  • 掌握Freemarker常用语法
  • Freemarker项目实战

什么是模版引擎: 数据 + 模版 = 结果, 模版引擎将数据与展现有效“解耦”.

image.png

在上图中,之前讲述的JSP其实就一个模版引擎,目前主流模版引擎: JSP 、 Freemarker、Beetl.
Freemarker 脚本为:FTL(Freemarker Template Language),Freemarker 提供了大量内建函数来简化开发.
JSP 与 Freemarker 区别

JSP Freemarker
官方标准
执行方式 编译型 解释型
执行效率
开发效率
扩展能力
数据提取 JSTL + EL 内置标签

Freemarker 快速上手

官方文档 首先我们要在官网上下载或通过maven来安装freemarker
新建一个Java项目,然后将下载的freemarker.jar包导入

Freemarker的使用主要有三步: 1 加载模版、2 创建数据、3 产生输出

  1. public class FreemarkerS1 {
  2. public static void main(String[] args) throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException, TemplateException {
  3. //1 加载模版
  4. //创建核心配置对象
  5. Configuration configuration = new Configuration(Configuration.VERSION_2_3_29);
  6. //设置加载的目录 在这个类所在的包中,加载ftl,第二个参数空字符串相当于当前的包 在当前类的包中查找ftl文件
  7. configuration.setClassForTemplateLoading(FreemarkerS1.class, "");
  8. //得到模版对象
  9. Template template = configuration.getTemplate("smaple.ftl");
  10. //2 创建数据
  11. Map<String,Object> data = new HashMap<String,Object>();
  12. data.put("site", "百度");
  13. data.put("url", "http://www.baidu.com");
  14. //3 产生输出
  15. template.process(data, new OutputStreamWriter(System.out));
  16. }
  17. }

新建FTL文件,可以选择安装插件

${site}
${url}

运行FreemarkerS1 类,得到的结果:
百度
http://www.baidu.com.

可见FTL文件提供模版,FreemarkerS1提供数据,然后通过加载模版 创建数据 就可以在模版中产生输出.

FTL 取值

  • ${属性名} 取值,可对属性进行计算
  • ${属性名!默认值} 使用默认值
  • ${属性名?string(“….”)} 格式化输出

如下代码:获取属性、格式化输出、默认值、对象的属性输出、以及map的输出等

<#-- freemarker取值 -->
${site}
${url}
<#-- freemarker默认值 -->
${author!"不存在的属性默认值:JakePrim"}
<#-- freemarker格式化 -->
${date?string("yyyy-MM-dd HH:mm:ss:SSS")}
${number}
${number?string("0.00")}
<#-- freemarker获取对象及属性 -->
${computer.sn}
${computer.model}
${computer.state}
${computer.user}
${computer.dop?string("yyyy-MM-dd")}
${computer.price?string("0.00")}
<#-- freemarker map的获取 -->
${computer.info["name"]}

分支判断

if 和 switch 语句分支判断,和Java语法没有区别

如下为if的使用:

<#-- 字符串是否相等的判断 直接使用 == -->
<#if computer.sn == "1111111">
重要设备:${computer.sn}
</#if>
${computer.sn}
${computer.model}
<#if computer.state == 1>
状态:正在使用
<#elseif computer.state == 2>
状态:闲置
<#elseif computer.state == 3>
状态:已作废
<#else>
状态:未知
</#if>
${computer.state}
<#-- ?? 代表判断对象是否为空,true不为空,false为空 -->
<#if computer.user??>
用户:${computer.user}
</#if>

如下为switch的使用:

<#switch computer.state>
    <#case 1>
        状态:正在使用
    <#break>    
    <#case 2>
        状态:闲置
    <#break>    
    <#case 3>
        状态:已作废
    <#break>    
    <#default>
        状态:未知
    <#break>    
</#switch>

list迭代列表

List<String> list = new ArrayList<String>();
        list.add("this1");
        list.add("this2");
        list.add("this3");
        data.put("list", list);

<#list list as c>
序号:${c_index} <#-- 迭代变量_index 保存了循环索引 -->
List:${c}
</#list>

输出:
序号:0 
List:this1
序号:1 
List:this2
序号:2 
List:this3

list迭代Map

Map<String,Object> map = new HashMap<String, Object>();
        int i = 0;
        for (String string : list) {
            i++;
            map.put("c_"+i, string);
        }
        data.put("map", map);
<#list map?keys as k>
${k}-${map[k]}
</#list>

输出:
c_1-this1
c_3-this3
c_2-this2

Freemarker内建函数

image.png

内建函数是Freemarker 提供的内部函数. 接下来通过代码来看一下:

public class FreemarkerS2 {
    public static void main(String[] args) throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException, TemplateException {
        //1 加载模版
        //创建核心配置对象
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_29);
        //设置加载的目录 在这个类所在的包中,加载ftl,第二个参数空字符串相当于当前的包 在当前类的包中查找ftl文件
        configuration.setClassForTemplateLoading(FreemarkerS2.class, "");
        //得到模版对象
        Template template = configuration.getTemplate("smaple2.ftl");
        //2 创建数据
        Map<String,Object> data = new HashMap<String,Object>();
        data.put("name", "jakePrim");
        data.put("brand", "bmw");
        data.put("words", "first blood");
        data.put("n", 878382.1281921);
        data.put("date", new Date());
        Map<String, Object> infoMap = new HashMap<String, Object>();
        infoMap.put("name", "jisdjsd");
        data.put("computer", new Computer("1001", "ThinkPad", 3, "JakePrim", new Date(), 5000.1f,infoMap));
        List<String> list = new ArrayList<String>();
        list.add("this1");
        list.add("this2");
        list.add("this3");
        data.put("list", list);
        //3 产生输出
        template.process(data, new OutputStreamWriter(System.out));
    }
}
${name?cap_first} <#-- 首字母大写 -->
${brand?upper_case} <#-- 全部大写 -->
${brand?length} <#-- 输出字符的长度 -->
${words?replace("blood","******")} <#-- 字符替换 -->
${words?index_of("blood")} <#-- 查找字符 -->
${(words?index_of("blood") != -1)?string("包含敏感词汇","不包含敏感词汇") } <#-- 借助?string可以使用三目运算符操作 -->

${n?round}
${n?floor}
${n?ceiling}

list 共有:${list?size}个
第一个:${list?first}
最后一个:${list?last}

其他的内建函数可以参考中文文档:中文文档

Freemarker 与 Servlet 整合

在上述中,我们通过在Java类中加载模版设置数据,那么Freemarker如何与Servlet整合呢? 这里Freemarker做了处理,可以非常简单和servlet进行整合.

  • 首先将freemarker.jar包,放到WEB-INF/lib中
  • 在web.xml进行配置
  <servlet>
      <servlet-name>freemarker</servlet-name>
      <servlet-class>freemarker.ext.servlet.FreemarkerServlet</servlet-class>
      <!-- 模版文件配置,所有的ftl都要在ftl目录下 -->
      <init-param>
          <param-name>TemplatePath</param-name>
          <param-value>/WEB-INF/ftl</param-value>
      </init-param>
  </servlet>
  <servlet-mapping>
      <servlet-name>freemarker</servlet-name>
      <url-pattern>*.ftl</url-pattern> <!-- 请求后缀为.ftl的文件,都会响应FreemarkerServlet -->
  </servlet-mapping>
  • servlet中配置数据,数据需要添加到request 或 session 或 servletContext的属性中,注意freemarker通过上述的配置会按顺序访问 request - session - servletContext 中设置的属性.
@WebServlet("/list")
public class ListServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public ListServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        List<Employee> employees = new ArrayList<Employee>();
        employees.add(new Employee(7731, "张三", "市场部", "客户代表", 8000f));
        employees.add(new Employee(7731, "李四", "技术部", "Java工程师", 18000f));
        request.setAttribute("employees", employees);//freemarker 会通过 查找request session servletContext
        request.getRequestDispatcher("/employee.ftl").forward(request, response);
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }

}
  • 编写ftl文件,可直接访问employees 进行数据设置

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>员工列表</title>
    <link href="css/bootstrap.css" type="text/css" rel="stylesheet"></link>

    <script type="text/javascript" src="js/jquery-1.11.1.min.js"></script>
    <script type="text/javascript" src="js/bootstrap.js"></script>

    <style type="text/css">
        .pagination {
            margin: 0px
        }

        .pagination > li > a, .pagination > li > span {
            margin: 0 5px;
            border: 1px solid #dddddd;
        }

        .glyphicon {
            margin-right: 3px;
        }

        .form-control[readonly] {
            cursor: pointer;
            background-color: white;
        }
        #dlgPhoto .modal-body{
            text-align: center;
        }
        .preview{

            max-width: 500px;
        }
    </style>
</head>
<body>

<div class="container">
    <div class="row">
        <h1 style="text-align: center">IMOOC员工信息表</h1>
        <div class="panel panel-default">
            <div class="clearfix panel-heading ">
                <div class="input-group" style="width: 500px;">

                </div>
            </div>

            <table class="table table-bordered table-hover">
                <thead>
                <tr>
                    <th>序号</th>
                    <th>员工编号</th>
                    <th>姓名</th>
                    <th>部门</th>
                    <th>职务</th>
                    <th>工资</th>
                    <th>&nbsp;</th>
                </tr>
                </thead>
                <tbody>
                <#list employees as em>
                    <tr>
                        <td>${em_index + 1}</td>
                        <td>${em.empno?string("0")}</td>
                        <td>${em.ename}</td>
                        <td>${em.department}</td>
                        <td>${em.job}</td>
                        <td style="color: red;font-weight: bold">¥${em.salary?string("0.00")}</td>
                    </tr>
                </#list>
                </tbody>
            </table>
        </div>
    </div>
</div>

</body>
</html>