FreeMaker入门

一、FreeMarker介绍

FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据,并用来生成输出文本(HTML网页、电子邮件配置文件源代码等)的通用工具。它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。

FreeMarker是免费的,基于Apache许可证2.0版本发布。其模板编写为FreeMarker Template Language(FTL),属于简单、专用的语言。需要准备数据在真实编程语言中来显示,比如数据库查询和业务运算,之后模板显示已经准备好的数据。在模板中,主要用于如何展现数据,而在模板之外注意于要展示什么数据 。

二、为什么使用FreeMarker

(一) FreeMarker的优点

  1. FreeMarker的优点一,可以彻底的分离表现层和业务逻辑。
    曾经在使用JSP 开发过程中发现在页面中大量的存在业务逻辑的代码,使得页面内容凌乱,在后期大量的修改维护过程中就变得非常困难。
    FreeMarker根本不支持Java脚本代码。
    FreeMarker的原理就是:模板+数据模型=输出,模板只负责数据在页面中的表现,不涉及任何的逻辑代码,而所有的逻辑都是由数据模型来处理的。用户最终看到的输出是模板和数据模型合并后创建的。

  2. FreeMarker的优点二,可以提高开发效率。
    根据以往的开发经验,使用的都是JSP 页面来展示数据的,即所谓的表现层。大家都知道,JSP在第一次执行的时候需要转换成Servlet类,开发阶段进行功能调适时,需要频繁的修改JSP,每次修改都要编译和转换,那么试想一天中我们浪费在程序编译的时间有多少。相对于JSP来说,FreeMarker模板技术不存在编译和转换的问题,所以就不会存在上述问题。而且开发过程中,大家在不必在等待界面设计开发人员完成页面原形后,大家再来开发程序。

  3. FreeMarker的优点三,使得开发过程中的人员分工更加明确。
    以往用JSP展现数据时,作为程序员的我们,并不熟悉界面设计技术,反之界面开发人员,也并不熟悉程序语言。对两者而言,交替性的工作本身就有难度。有时候稍有不慎,可能会将某个页面元素删除或去掉了某个程序符号,使得页面走样或程序错误,这样就需要双方相互沟通协作,解决出现的问题。有时候因为项目中的时间,任务量等因素的存在,可能这个工作就由一个人来完成,这样就可能加大某一方开发人员的工作量。
    使用FreeMarker后,作为界面开发人员,只专心创建HTML文件、图像以及Web页面的其他可视化方面,不用理会数据;而程序开发人员则专注于系统实现,负责为页面准备要显示的数据。

(二)FreeMarker的缺点

  1. FreeMarker的缺点一,应用FreeMarker模板技术,在修改模板后,可能会看到已经过期的数据。如:生成静态的HTML页面后,如果一旦模板改变,而没有及时更新模板生成的HTML页面的话,用户看到的就是过期的数据。
  2. FreeMarker的缺点二,FreeMarker模板技术在应用过程中,FreeMarker中的变量必须要赋值,如果不赋值,那么就会抛出异常。想避免错误就要应用if/elseif/else 指令进行判段,如果对每一个变量都判断的话,那么则反而增加了编程的麻烦。
  3. FreeMarker的缺点三,FreeMarker的map限定key必须是string,其他数据类型无法操作。
  4. FreeMarker的缺点四,FreeMarker不支持集群应用。为了编成的方便性,把序列化的东西都放到了Session中,如Session,request等,在开发的过程中确实方便,但如果将应用放到集群中,就会出现错误。

三、FreeMarker快速入门

freemarker作为springmvc一种视图格式,默认情况下SpringMVC支持freemarker视图格式。

需要创建Spring Boot+Freemarker工程用于测试模板。

3.1 创建测试工程

创建一个freemarker 的测试工程专门用于freemarker的功能测试与模板的测试。

pom.xml如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>xc-framework-parent</artifactId>
        <groupId>com.xuecheng</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../xc-framework-parent/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>test‐freemarker</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
        </dependency>
    </dependencies>

</project>

3.2 创建模型类

package com.xuecheng.test.freemarker.model;

import lombok.Data;
import lombok.ToString;

import java.util.Date;
import java.util.List;

/**
 * @author 98050
 */
@Data
@ToString
public class Student {
    private String name;//姓名
    private int age;//年龄
    private Date birthday;//生日
    private Float mondy;//钱包
    private List<Student> friends;//朋友列表
    private Student bestFriend;//最好的朋友
}

3.3 创建模板

在 src/main/resources下创建templates,此目录为freemarker的默认模板存放目录。

在templates下创建模板文件test1.ftl,模板中的${name}最终会被freemarker替换成具体的数据。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello World!</title>
</head>
<body>
Hello ${name}!
</body>
</html>

3.4 创建Controller

package com.xuecheng.test.freemarker.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Map;

/**
 * @Author: 98050
 * @Time: 2019-03-28 14:27
 * @Feature:
 */
@RequestMapping("/freemarker")
@Controller
public class FreemarkerController {


    @RequestMapping("/test1")
    public String freemarker(Map<String,Object> map){
        map.put("name", "李四");
        return "test1";
    }
}

3.5 创建启动类

package com.xuecheng.test.freemarker;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @Author: 98050
 * @Time: 2019-03-28 14:30
 * @Feature:
 */
@SpringBootApplication
public class FreemarkerTestApplication {

    public static void main(String[] args) {
        SpringApplication.run(FreemarkerTestApplication.class,args);
    }
}

3.6 测试

访问:http://localhost:8088/freemarker/test1

结果:

四、FreeMarker基础

4.1 核心指令

4.1.1 数据模型

Freemarker静态化依赖数据模型和模板,下边定义数据模型:

下边方法形参map即为freemarker静态化所需要的数据模型,在map中填充数据:

@RequestMapping("/test1")
public String freemarker(Map<String,Object> map){
    //向数据模型放数据
    map.put("name","李四");
    Student stu1 = new Student();
    stu1.setName("小明");
    stu1.setAge(18);
    stu1.setMondy(1000.86f);
    stu1.setBirthday(new Date());
    Student stu2 = new Student();
    stu2.setName("小红");
    stu2.setMondy(200.1f);
    stu2.setAge(19);
    stu2.setBirthday(new Date());
    List<Student> friends = new ArrayList<>();
    friends.add(stu1);
    stu2.setFriends(friends);
    stu2.setBestFriend(stu1);
    List<Student> stus = new ArrayList<>();
    stus.add(stu1);
    stus.add(stu2);
    //向数据模型放数据
    map.put("stus",stus);
    //准备map数据
    HashMap<String,Student> stuMap = new HashMap<>();
    stuMap.put("stu1",stu1);
    stuMap.put("stu2",stu2);
    //向数据模型放数据
    map.put("stu1",stu1);
    //向数据模型放数据
    map.put("stuMap",stuMap);
    //返回模板文件名称
    return "test1";
}

4.1.2 List指令

本节定义freemarker模板,模板中使用freemarker的指令,关于freemarker的指令需要知道:

1、注释,即<#‐‐和‐‐>,介于其之间的内容会被freemarker忽略

2、插值(Interpolation):即${..}部分,freemarker会用真实的值代替​${…}

3、FTL指令:和HTML标记类似,名字前加#予以区分,Freemarker会解析标签中的表达式或逻辑。

4、文本,仅文本信息,这些不是freemarker的注释、插值、FTL指令的内容会被freemarker忽略解析,直接输出内容

在test1.ftl模板中使用list指令遍历数据模型中的数据:

<table>
    <tr>
        <td>序号</td>
        <td>姓名</td>
        <td>年龄</td>
        <td>钱包</td>
    </tr>
    <#list stus as stu>
        <tr>
            <td>${stu_index + 1}</td>
            <td>${stu.name}</td>
            <td>${stu.age}</td>
            <td>${stu.mondy}</td>
        </tr>
    </#list>

</table>

结果:

说明:

_index:得到循环的下标,使用方法是在stu后边加"_index",它的值是从0开始

4.1.3 遍历Map数据

1、数据模型

使用map指令遍历数据模型中的stuMap。

2、模板

<br/><br/>
输出stu1的学生信息:<br/>
姓名:${stuMap['stu1'].name}<br/>
年龄:${stuMap['stu1'].age}<br/>
输出stu1的学生信息:<br/>
姓名:${stu1.name}<br/>
年龄:${stu1.age}<br/>
遍历输出两个学生信息:<br/>
<table>
    <tr>
        <td>序号</td>
        <td>姓名</td>
        <td>年龄</td>
        <td>钱包</td>
    </tr>
    <#list stuMap?keys as k>
        <tr>
            <td>${k_index + 1}</td>
            <td>${stuMap[k].name}</td>
            <td>${stuMap[k].age}</td>
            <td >${stuMap[k].mondy}</td>
        </tr>
    </#list>
</table>

结果:

4.1.4 if指令

if 指令即判断指令,是常用的FTL指令,freemarker在解析时遇到if会进行判断,条件为真则输出if中间的内容,否则跳过内容不再输出。

1、数据模型

使用list指令中测试数据模型

2、模板

<table>
    <tr>
        <td>序号</td>
        <td>姓名</td>
        <td>年龄</td>
        <td>钱包</td>
    </tr>
    <#list stus as stu>
        <tr>
            <td <#if stu.name == '小明'>style="background: red;" </#if>>${stu_index + 1}</td>
            <td>${stu.name}</td>
            <td>${stu.age}</td>
            <td>${stu.mondy}</td>
        </tr>
    </#list>
</table>

3、测试

4.2 其它指令

4.2.1 运算符

1、算数运算符 FreeMarker表达式中完全支持算术运算,FreeMarker支持的算术运算符包括:+, - , * , / , %

2、逻辑运算符 逻辑运算符有如下几个: 逻辑与:&& 逻辑或:|| 逻辑非:! 逻辑运算符只能作用于布尔值,否则将产生错误

3、比较运算符 表达式中支持的比较运算符有如下几个:

1 =或者==:判断两个值是否相等.

2 !=:判断两个值是否不等.

3 > 或者gt:判断左边值是否大于右边值

4 >=或者gte:判断左边值是否大于等于右边值

5 <或者lt:判断左边值是否小于右边值

6 <=或者lte:判断左边值是否小于等于右边值

注意: =和!=可以用于字符串,数值和日期来比较是否相等,但=和!=两边必须是相同类型的值,否则会产生错误,而且FreeMarker是精确比较,“x”,"x ","X"是不等的.其它的运行符可以作用于数字和日期,但不能作用于字符串,大部分的时候,使用gt等字母运算符代替>会有更好的效果,因为 FreeMarker会把>解释成FTL标签的结束字符,当然,也可以使用括 号来避免这种情况,如:<#if (x>y)>

4.2.2 空值处理

1、判断某变量是否存在使用 “??” 用法为:variable??,如果该变量存在,返回true,否则返回false

2、缺失变量默认值使用 “!” 使用!要以指定一个默认值,当变量为空时显示默认值。

4.2.3 内建函数

内建函数语法格式: 变量+?+函数名称

1、得到集合的大小

${集合名?size}

2、日期格式化

显示年月日: ${today?date}

显示时分秒:​${today?time}

显示日期+时间:​${today?datetime}

自定义格式化: ​${today?string(“yyyy年MM月”)}

3、内建函数c

map.put(“point”, 102920122);
point是数字型,使用${point}会显示这个数字的值,并且每三位使用逗号分隔。
如果不想显示为每三位分隔的数字,可以使用c函数将数字型转成字符串输出​${point?c}

4、将json字符串转成对象

<#assign text="{'bank':'工商银行','account':'10101920201920212'}" />
<#assign data=text?eval />开户行:{data.bank} 账号:{data.account}

其中用到了 assign标签,assign的作用是定义一个变量。

4.3 静态化测试

在cms中使用freemarker将页面生成html文件,本节测试html文件生成的方法:

1、使用模板文件静态化

定义模板文件,使用freemarker静态化程序生成html文件。

2、使用模板字符串静态化

定义模板字符串,使用freemarker静态化程序生成html文件。

4.3.1 使用模板文件静态化

在test下创建测试类,并且将main下的resource/templates拷贝到test下,本次测试使用之前我们在main下创建的模板文件

import com.xuecheng.test.freemarker.model.Student;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;


/**
 * @Author: 98050
 * @Time: 2019-03-28 15:28
 * @Feature:
 */
public class FreemarkerTest {

    @Test
    public void testGenerateHtml() throws IOException, TemplateException {
        //1.创建配置类
        Configuration configuration = new Configuration(Configuration.getVersion());
        //2.设置模板路径
        String classpath = this.getClass().getResource("/").getPath();
        configuration.setDirectoryForTemplateLoading(new File(classpath + "/templates/"));
        //3.设置字符集
        configuration.setDefaultEncoding("utf-8");
        //4.加载模板
        Template template = configuration.getTemplate("test1.ftl");
        //5.数据模型
        Map<String,Object> map = getData();
        //6.静态化
        String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);
        //7.静态化内容
        System.out.println(content);
        InputStream inputStream = IOUtils.toInputStream(content);
        //8.输出文件
        FileOutputStream fileOutputStream = new FileOutputStream(new File("d:/test1.html"));
        int copy = IOUtils.copy(inputStream, fileOutputStream);
    }

    private Map<String, Object> getData() {
        Map<String,Object> map = new HashMap<>();
        //向数据模型放数据
        map.put("name","李四");
        Student stu1 = new Student();
        stu1.setName("小明");
        stu1.setAge(18);
        stu1.setMondy(1000.86f);
        stu1.setBirthday(new Date());
        Student stu2 = new Student();
        stu2.setName("小红");
        stu2.setMondy(200.1f);
        stu2.setAge(19);
        stu2.setBirthday(new Date());
        List<Student> friends = new ArrayList<>();
        friends.add(stu1);
        stu2.setFriends(friends);
        stu2.setBestFriend(stu1);
        List<Student> stus = new ArrayList<>();
        stus.add(stu1);
        stus.add(stu2);
        //向数据模型放数据
        map.put("stus",stus);
        //准备map数据
        HashMap<String,Student> stuMap = new HashMap<>();
        stuMap.put("stu1",stu1);
        stuMap.put("stu2",stu2);
        //向数据模型放数据
        map.put("stu1",stu1);
        //向数据模型放数据
        map.put("stuMap",stuMap);
        return map;
    }
}

结果:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello World!</title>
</head>
<body>
Hello 李四!

<table>
    <tr>
        <td>序号</td>
        <td>姓名</td>
        <td>年龄</td>
        <td>钱包</td>
    </tr>
        <tr>
            <td>1</td>
            <td>小明</td>
            <td>18</td>
            <td>1,000.86</td>
        </tr>
        <tr>
            <td>2</td>
            <td>小红</td>
            <td>19</td>
            <td>200.1</td>
        </tr>
</table>

<br/><br/>
输出stu1的学生信息:<br/>
姓名:小明<br/>
年龄:18<br/>
输出stu1的学生信息:<br/>
姓名:小明<br/>
年龄:18<br/>
遍历输出两个学生信息:<br/>
<table>
    <tr>
        <td>序号</td>
        <td>姓名</td>
        <td>年龄</td>
        <td>钱包</td>
    </tr>
        <tr>
            <td>1</td>
            <td>小红</td>
            <td>19</td>
            <td >200.1</td>
        </tr>
        <tr>
            <td>2</td>
            <td>小明</td>
            <td>18</td>
            <td >1,000.86</td>
        </tr>
</table>
<br /><br />
<table>
    <tr>
        <td>序号</td>
        <td>姓名</td>
        <td>年龄</td>
        <td>钱包</td>
    </tr>
        <tr>
            <td style="background: red;" >1</td>
            <td>小明</td>
            <td>18</td>
            <td>1,000.86</td>
        </tr>
        <tr>
            <td >2</td>
            <td>小红</td>
            <td>19</td>
            <td>200.1</td>
        </tr>
</table>
</body>
</html>

4.3.2 使用模板字符串静态化

/**
 * 基于模板字符串生成静态文件
 * @throws IOException
 * @throws TemplateException
 */
@Test
public void testGenerateHtmlByString() throws IOException, TemplateException {
    //1.创建配置类
    Configuration configuration = new Configuration(Configuration.getVersion());
    //2.模板内容
    String templateString="" +
            "<html>\n" +
            "    <head></head>\n" +
            "    <body>\n" +
            "    名称:${name}\n" +
            "    </body>\n" +
            "</html>";
    //3.模板加载器
    StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
    stringTemplateLoader.putTemplate("template", templateString);
    configuration.setTemplateLoader(stringTemplateLoader);
    //4.得到模板
    Template template = configuration.getTemplate("template","utf-8");
    //5.数据模型
    Map<String,Object> map = new HashMap<>();
    map.put("name", "李四");
    //6.静态化
    String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);
    //7.静态化内容
    System.out.println(content);
    InputStream inputStream = IOUtils.toInputStream(content);
    //8.输出文件
    FileOutputStream fileOutputStream = new FileOutputStream(new File("d:/test2.html"));
    int copy = IOUtils.copy(inputStream, fileOutputStream);
}

结果:

<html>
    <head></head>
    <body>
    名称:李四
    </body>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值