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-freemarkerspring-boot-starter-weblombokokhttpcommons-iospring-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 算数运算符
- +
 - -
 - *
 - /
 - %
 
3.2.1.2 逻辑运算符
- 与:
&& - 或:
|| - 非:
! 
3.1.2.3 比较运算符
===!=>或gt>=或gte<或lt<=或lte
注意:
=和!=可以用于字符串,数值和日期来比较是否相等,但=和!=两边必须是相同类型的值,否则会产生错误- 使用
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} 1234567893.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;
    }
One comment
想想你的文章写的特别好https://www.ea55.com/