1 FreeMarker介绍

  • FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页、电子邮件、配置文件、源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
  • FreeMarker是免费的,基于Apache许可证2.0版本发布。其模板编写为FreeMarker Template Language(FTL),属于简单、专用的语言。需要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,主要用于如何展现数据, 而在模板之外注意于要展示什么数据

1.1 常见的模板引擎

  • JSP
  • Thymeleaf
  • Velocity

1.2 模板+数据模型=输出

  • freemarker并不关心数据的来源,只是根据模板的内容,将数据模型在模板中显示并输出文件(通常为html,也 可以生成其它格式的文本文件)

1.2.1 数据模型

  • 数据模型在java中可以是基本类型也可以List、Map、Pojo等复杂类型。

1.2.2 模板

1.2.3 输出

2 FreeMarker快速入门

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

2.1 pom.xml

  • spring-boot-starter-freemarker
  • spring-boot-starter-web
  • lombok
  • okhttp
  • commons-io
  • spring-boot-starter-test
<?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.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>

</project>

2.2 配置文件application.yml

server:
  port: 8088 #服务端口
spring:
  application:
    name: test-freemarker #指定服务名
  freemarker:
    cache: false #关闭模板缓存,方便测试
    settings:
      template_update_delay: 0 #检查模板更新延迟时间,设置为0表示立即检查,如果时间大于0会有缓存不方便进行模板测试

2.3 创建模型类Student

@Data
@ToString
public class Student {
    private String name;
    private int age;
    private Date birthday;
    private Float money;
    private List<Student> friends;
    private Student bestFriend;
}

2.4 创建模板src/resources/templates/test1.ftl

  • 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>

2.5 创建controller

    @RequestMapping("/test1")
    public String test1(Map<String, Object> map) {
    
        //map形参存在request域中,是freemarker模板使用的数据
        map.put("name", "张三");

        //返回freemarker模板的位置,基于resource/templates路径
        return "test1";
    }

2.6 创建启动类

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

3 FreeMarker基础

3.1 核心指令

3.1.1 数据模型

  • Freemarker静态化依赖数据模型和模板,下边定义数据模型。下边方法形参map即为freemarker静态化所需要的数据模型,在map中填充数据:
    @RequestMapping("/test1")
    public String test1(Map<String, Object> map) {
        //String
        //map形参存在request域中,是freemarker模板使用的数据
        map.put("name", "张三");

        Student stu1 = new Student();
        stu1.setAge(18);
        stu1.setBirthday(new Date());
        stu1.setMoney(200F);
        stu1.setName("小红");

        Student stu2 = new Student();
        stu2.setAge(19);
        stu2.setBirthday(new Date());
        stu2.setMoney(100F);
        stu2.setName("小方");

        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);
        
        //list
        //将学生列表放在数据模型中
        map.put("stus", stus);

        Map<String, Student> stuMap = new HashMap<>();
        stuMap.put("stu1", stu1);
        stuMap.put("stu2", stu2);

        //向数据模型放数据
        //model
        map.put("stu1", stu1);
        //map
        map.put("stuMap", stuMap);

        //存放数字
        //number
        map.put("point", 123456789);

        //返回freemarker模板的位置,基于resource/templates路径
        return "test1";
    }

3.1.2 基础指令

3.1.2.1 注释

<#--被注释的内容-->

3.1.2.2 插值

${name}

3.1.2.3 FTL指令

<#if></#if>

3.1.3 List指令

遍历数据模型中的list学生信息,(stus)
<table>
    <tr>
        <td>序号</td>
        <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.money}</td>
            <td>${stu.age}</td>
            <td>${stu.birthday?date}</td>
        </tr>
    </#list>
</table>
  • 注意:[item]_index得到循环的下标,值从0开始

3.1.4 遍历Map数据

<!-- 第一种方法:在中括号中填写map的key -->    
姓名:${stuMap['stu1'].name}<br>
年龄:${stuMap['stu1'].age}<br>
<!-- 第二种方法:在map后直接“点key” --> 
姓名:${stuMap.stu2.name}<br>
年龄:${stuMap.stu2.age}<br>

<table>
    <tr>
        <td>序号</td>
        <td>姓名</td>
        <td>年龄</td>
        <td>金额</td>
    </tr>
    <!-- 遍历map中的key, stuMap?keys是key列表(是一个list) -->
    <#list stuMap?keys as k>
        <tr>
            <td>${k_index+1}</td>
            <td>${stuMap[k].name}</td>
            <td>${stuMap[k].age}</td>
            <td>${stuMap[k].money}</td>
        </tr>
    </#list>
</table>

3.1.5 if指令

  • if 指令即判断指令,是常用的FTL指令,freemarker在解析时遇到if会进行判断,条件为真则输出if中间的内容,否 则跳过内容不再输出。
<!-- 如果stus存在-->
<#if stus?? >
    <#list stus as stu>
        <tr>
            <td>${stu_index+1}</td>
            <!-- 如果name为小方,背景色为蓝色-->
            <td <#if stu.name == "小方"> style="background: cadetblue"</#if>>${stu.name}</td>
<td>${stu.money}</td>
            <!-- 大于号> 会和标签的尖括号混淆,可以用括号(stu.money > 300) 括起来,或者用gt代替>-->
            <!-- 如果money大于150,背景色为蓝色-->
            <td <#if stu.money gt 150> style="background: cadetblue"</#if>>${stu.age}</td>
            <td>${stu.birthday?date}</td>
        </tr>
    </#list>
</#if>

3.2 其他指令

3.2.1 运算符

3.2.1.1 算数运算符

  1. +
  2. -
  3. *
  4. /
  5. %

3.2.1.2 逻辑运算符

  1. 与:&&
  2. 或:||
  3. 非:!

3.1.2.3 比较运算符

  1. =
  2. ==
  3. !=
  4. >gt
  5. >=gte
  6. <lt
  7. <=lte

注意:

  1. =!=可以用于字符串,数值和日期来比较是否相等,但=!=两边必须是相同类型的值,否则会产生错误
  2. 使用gt等字母运算符代替>会有更好的效果,因为 FreeMarker会把>解释成FTL标签的结束字符,当然,也可以使用括 号来避免这种情况
<#if (x>y)>

3.2.2 空值处理

  • 判断某变量是否存在使用 ??。用法为:variable??,如果该变量存在,返回true,否则返回false
<#if stus?? >
    ...
</#if>
  • 缺失变量默认值使用 ! 使用!要以指定一个默认值,当变量为空时显示默认值。
<!-- 如果name为空显示空字符串-->
${name!''} 

<!-- 如果是嵌套对象则用()括起来-->
${(stuMap['stu1'].name)!''}

3.2.3 内建函数

  • 语法:变量?函数名称

3.2.3.1 集合的大小

${集合名?size}

3.2.3.2 日期格式化

显示年月日: ${today?date}
显示时分秒: ${today?time}
显示日期+时间: ${today?datetime}
自定义格式化: ${today?string("yyyy年MM月")}

3.2.3.3 c (数字不用逗号分隔)

<!--c函数将数字型转为字符串,123,456,789 -> 123456789-->
${point} 123,456,789
${point?c} 123456789

3.2.4 json转为对象 assign eval

  • assign定义变量
  • eval转化为对象
<#--json转对象-->
<#assign text="{'name':'json_name','age':'20'}" />
<#assign data=text?eval />
name:${data.name} <br>
age:${data.age} <br>

4 静态化

4.1 使用模板文件静态化

    //基于ftl模板生成html文件
    @Test
    public void testGenerateHtml() throws IOException, TemplateException {
        //1. 定义配置类
        Configuration configuration = new Configuration(Configuration.getVersion());
        //2. 定义模板
        //2.1 得到classpath的路径
        String classpath = this.getClass().getResource("/").getPath();
        //2.2 定义模板路径
        configuration.setDirectoryForTemplateLoading(new File(classpath + "/templates/"));
        //2.3 获取模板文件的内容
        Template template = configuration.getTemplate("test1.ftl");
        //3. 定义数据模型
        Map map = getMap();

        //4. 静态化
        String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);
//        System.out.println(content);

        InputStream inputStream = IOUtils.toInputStream(content);
        FileOutputStream outputStream = new FileOutputStream(new File("d:/prac_project/test1.html"));

        //输出文件
        IOUtils.copy(inputStream, outputStream);
        inputStream.close();
        outputStream.close();
    }

4.2 使用模板字符串静态化(建议)

//根据模板内容(字符串)生成html
    @Test
    public void testGenerateHtmlByString() throws IOException, TemplateException {
        //1. 定义配置类
        Configuration configuration = new Configuration(Configuration.getVersion());
        //2. 定义模板
        //2.1 模板内容(字符串)
        String templateString = "" +
                "<html>\n" +
                "    <head></head>\n" +
                "    <body>\n" +
                "    名称:${name}\n" +
                "    </body>\n" +
                "</html>";
        //2.2 使用模板加载器将字符串变为模板
        StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
        stringTemplateLoader.putTemplate("template", templateString);
        //2.3 在配置中设置模板加载器
        configuration.setTemplateLoader(stringTemplateLoader);
        //2.4 获得模板
        Template template = configuration.getTemplate("template", "utf-8");

        //3. 定义数据模型
        Map map = getMap();

        //4. 静态化
        String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);
//        System.out.println(content);

        InputStream inputStream = IOUtils.toInputStream(content);
        FileOutputStream outputStream = new FileOutputStream(new File("d:/prac_project/test1.html"));

        //输出文件
        IOUtils.copy(inputStream, outputStream);
        inputStream.close();
        outputStream.close();
    }
    //获取数据模型
    public Map getMap() {
        Map map = new HashMap();
        //map形参存在request域中,是freemarker模板使用的数据
        map.put("name", "张三");

        Student stu1 = new Student();
        stu1.setAge(18);
        stu1.setBirthday(new Date());
        stu1.setMoney(200F);
        stu1.setName("小红");

        Student stu2 = new Student();
        stu2.setAge(19);
        stu2.setBirthday(new Date());
        stu2.setMoney(100F);
        stu2.setName("小方");

        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<String, Student> stuMap = new HashMap<>();
        stuMap.put("stu1", stu1);
        stuMap.put("stu2", stu2);

        //向数据模型放数据
        map.put("stu1", stu1);
        map.put("stuMap", stuMap);

        //存放数字
        map.put("point", 123456789);

        return map;
    }
Last modification:February 3rd, 2020 at 08:18 pm