本文最后更新于 167 天前 ,文中信息可能已经过时。如有问题请在评论区留言。

简介

Apache FreeMarker™ 是一个模板引擎:一个基于模板和变更数据生成文本输出(HTML 网页、电子邮件、配置文件、源代码等)的 Java 库。

FreeMarker 没有其他任何依赖,仅仅依赖 Java 自身。

最新版本:FreeMarker 2.3.32(2023-01-12)

maven 依赖

普通 Java 工程按如下方式引入:

xml
pom.xml
1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.32</version>
</dependency>

如果是 Spring Boot 工程可以引入 spring-boot-starter-freemarker

xml
pom.xml
1
2
3
4
5
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-freemarker -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

代码示例

普通工程

以下代码来源于 FreeMarker 官方教程

java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import freemarker.template.*;
import java.util.*;
import java.io.*;

public class Test {

    public static void main(String[] args) throws Exception {

        /* ------------------------------------------------------------------------ */
        /* You should do this ONLY ONCE in the whole application life-cycle:        */

        /* Create and adjust the configuration singleton */
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_32);
        cfg.setDirectoryForTemplateLoading(new File("/where/you/store/templates"));
        // Recommended settings for new projects:
        cfg.setDefaultEncoding("UTF-8");
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        cfg.setLogTemplateExceptions(false);
        cfg.setWrapUncheckedExceptions(true);
        cfg.setFallbackOnNullLoopVariable(false);
        cfg.setSQLDateAndTimeTimeZone(TimeZone.getDefault());

        /* ------------------------------------------------------------------------ */
        /* You usually do these for MULTIPLE TIMES in the application life-cycle:   */

        /* Create a data-model */
        Map root = new HashMap();
        root.put("user", "Big Joe");
        Product latest = new Product();
        latest.setUrl("products/greenmouse.html");
        latest.setName("green mouse");
        root.put("latestProduct", latest);

        /* Get the template (uses cache internally) */
        Template temp = cfg.getTemplate("test.ftlh");

        /* Merge data-model with template */
        Writer out = new OutputStreamWriter(System.out);
        temp.process(root, out);
        // Note: Depending on what `out` is, you may need to call `out.close()`.
        // This is usually the case for file output, but not for servlet output.
    }
}

Product 类如下:

java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
 * Product bean; note that it must be a public class!
 */
public class Product {

    private String url;
    private String name;

    // As per the JavaBeans spec., this defines the "url" bean property
    // It must be public!
    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    // As per the JavaBean spec., this defines the "name" bean property
    // It must be public!
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

FreeMarker 模板 test.ftlh 如下:

html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<html lang="en">
  <head>
    <title>Welcome!</title>
  </head>
  <body>
    <h1>Welcome ${user}!</h1>
    <p>Our latest product:
    <a href="${latestProduct.url}">${latestProduct.name}</a>!
  </body>
</html>

Spring Boot 工程

温馨提示

Spring Boot 默认使用 .ftlh 作为后缀,如果需要修改为 .ftl,请按如下修改配置:

properties
1
spring.freemarker.suffix=.ftl

注意:上述配置中一定不要忘记点号 (.)。

在 Spring Boot 中实现上述功能(需引入 spring-boot-starter-freemarker 依赖):

java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;

@Component
public class TestRunner implements ApplicationRunner {

    private Configuration configuration;

    @Autowired
    public void setConfiguration(Configuration configuration) {
        this.configuration = configuration;
    }


    @Override
    public void run(ApplicationArguments args) throws Exception {
        Map root = new HashMap();
        root.put("user", "Big Joe");
        Product latest = new Product();
        latest.setUrl("products/greenmouse.html");
        latest.setName("green mouse");
        root.put("latestProduct", latest);

        Template temp = configuration.getTemplate("test.ftlh");

        Writer out = new OutputStreamWriter(System.out);
        temp.process(root, out);
    }
}

Product 类(使用 Lombok):

java
1
2
3
4
5
6
7
import lombok.Data;

@Data
public class Product {
    private String url;
    private String name;
}

模板文件放在 /resources/templates 目录下。

提示
Spring Boot 默认封装了 FreeMarker 配置,我们可以直接注入使用。也可以修改 FreeMarker 配置,如缓存、模板路径、模板文件后缀等。配置节点以 spring.freemarker 为前缀。

如果你希望使用 MVC 的方式直接加载这个页面,可以参考如下:

java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class TestController {

    @GetMapping("/")
    public String test(Model model) {
        Product latest = new Product();
        latest.setUrl("products/greenmouse.html");
        latest.setName("green mouse");
        model.addAttribute("user", "Big Joe");
        model.addAttribute("latestProduct", latest);
        return "test";
    }
}

运行项目,打开 http://localhost:8080 即可渲染效果。

模板语法

提示
具体详细的语法及说明应参考官方文档

模板结构

FTL 模板由以下部分混合而成:

  • 文本(Text): 文本内容会原样输出。
  • 插值(Interpolation:这部分的输出会被计算结果替代。插值使用 ${...}。(注:#{...} 已被弃用。See more here
  • FTL 标签(FTL tags):与 HTML 标签相似,用于 FreeMarker 指令。
  • 注释(Comments):使用 <#----> 来分隔。注释会被 FreeMarker 直接忽略,不会在输出内容中显示。

插值

标准格式为:${expression} 。其中 expression 可以是所有类型的表达式。如:${100 + x}

提示
插值表达式也可以使用 [=expression] 形式。参考 See more about alternative syntaxes…

表达式

内置函数参考

指令

标准格式是使用两种 FTL 标签:

  • 开始标签:<#directivename parameters>
  • 结束标签:</#directivename>

例如:<#if something>...</#if>

FTL 标签与 HTML 标签一样,必须正确嵌套。所以下面的代码是错误的,因为 if 指令既在 list 指令的嵌套内容的内部又在外部:

html
1
2
3
4
5
6
7
8
<ul>
<#list animals as animal>
  <li>${animal.name} for ${animal.price} Euros
  <#if user == "Big Joe">
     (except for you)
</#list> <#-- WRONG! The "if" has to be closed first. -->
</#if>
</ul>
提示

FreeMarker 可以使用 [] 替代 <>。例如:[#if user == "Big Joe"]...[/#if]

更多信息请阅读:Miscellaneous/Alternative (square bracket) syntax.

指令参考:

参考

附录

FreeMarker 支持的转义字符

Escape sequenceMeaning
\"引号 (u0022)
\'单引号 (u0027)
\{起始花括号:{
\=等号:= (Supported since FreeMarker 2.3.28.)
\\反斜杠 (u005C)
\n换行符 (u000A)
\r回车 (u000D)
\t水平制表符(又称为tab) (u0009)
\b退格 (u0008)
\f换页 (u000C)
\l小于号:<
\g大于号:>
\a& 符:&
\xCode字符的十六进制Unicode 码 (UCS code)

\x 之后的 Code 是 1-4 位的十六进制码。下面这个示例中都是在字符串中放置版权符号: "\xA9 1999-2001""\x0A9 1999-2001""\x00A9 1999-2001"。 如果紧跟十六进制码后一位的字符也能解释成十六进制码时, 就必须把 4 位补全,否则 FreeMarker 就会误解你的意图。

FTL 中的保留名称

下面的这些名称不能在非方括号语法中被用作顶层变量 (比如 .vars["in"]),因为这是 FTL 中的关键字:

  • true:布尔值"true"
  • false:布尔值"false"
  • gt:比较运算符"大于"
  • gte:比较运算符"大于或等于"
  • lt:比较运算符"小于"
  • lte:比较运算符"小于或等于"
  • as:由少数指令使用
  • in:由少数指令使用
  • using:由少数指令使用