FreeMarker 是一个基于 Java 的模板引擎,广泛用于动态内容生成,尤其适用于 Web 应用程序中的视图层。它通过将业务逻辑与表示逻辑分离,提升了开发效率和代码可维护性。


一、核心概念

1.1 模板(Template)

  • FreeMarker 模板是文本文件(如 HTML、XML、JSON 等),包含静态内容和 FreeMarker 指令。
  • 使用 .ftl 作为文件扩展名。

1.2 数据模型(Data Model)

  • 一个 Java 对象或 Map,包含模板中使用的变量。
  • FreeMarker 通过反射机制访问这些变量。

1.3 指令(Directives)

  • 控制结构(如 if, list, assign)和宏(macro)等。
  • <#...> 形式出现。

1.4 指令类型

  • 控制指令<#if>, <#list>, <#switch>
  • 赋值指令<#assign>, <#local>
  • 宏与函数<#macro>, <#function>
  • 导入与包含<#include>, <#import>
  • 空处理??, !, ??

1.5 模板加载与缓存

  • FreeMarker 支持从文件系统、类路径、数据库等加载模板。
  • 支持模板缓存机制,提升性能。

二、操作步骤(详细)

2.1 引入依赖

Maven 项目:

<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.32</version> <!-- 使用最新稳定版本 -->
</dependency>

Gradle 项目:

implementation 'org.freemarker:freemarker:2.3.32'

2.2 创建模板文件(如:hello.ftl

<html>
<head><title>FreeMarker Example</title></head>
<body>
  <h1>Hello, ${name}!</h1>
  <ul>
  <#list items as item>
    <li>${item}</li>
  </#list>
  </ul>
</body>
</html>

保存路径如:src/main/resources/templates/hello.ftl


2.3 配置 FreeMarker 配置对象(Configuration

import freemarker.template.Configuration;
import freemarker.template.TemplateExceptionHandler;

public class FreeMarkerConfig {
    public static Configuration getConfiguration() throws Exception {
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_32);
        // 设置模板路径(文件系统路径)
        // cfg.setDirectoryForTemplateLoading(new File("/path/to/templates"));

        // 或者从 classpath 加载模板
        cfg.setClassForTemplateLoading(FreeMarkerConfig.class, "/templates");

        // 设置默认编码
        cfg.setDefaultEncoding("UTF-8");
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        cfg.setLogTemplateExceptions(false);
        cfg.setWrapUncheckedExceptions(true);
        return cfg;
    }
}

2.4 准备数据模型

import java.util.HashMap;
import java.util.Map;

public class DataModel {
    public static Map<String, Object> getModel() {
        Map<String, Object> model = new HashMap<>();
        model.put("name", "John Doe");
        model.put("items", List.of("Apple", "Banana", "Cherry"));
        return model;
    }
}

2.5 渲染模板

import freemarker.template.Template;
import freemarker.template.Configuration;
import java.io.StringWriter;
import java.util.Map;

public class TemplateRenderer {
    public static String renderTemplate(String templateName, Map<String, Object> model) throws Exception {
        Configuration cfg = FreeMarkerConfig.getConfiguration();
        Template template = cfg.getTemplate(templateName);

        StringWriter writer = new StringWriter();
        template.process(model, writer);
        return writer.toString();
    }
}

2.6 主程序调用示例

public class MainApp {
    public static void main(String[] args) {
        try {
            Map<String, Object> model = DataModel.getModel();
            String html = TemplateRenderer.renderTemplate("hello.ftl", model);
            System.out.println(html);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

三、常见错误与解决方法

错误类型 描述 解决方法
TemplateNotFoundException 找不到模板文件 检查模板路径是否正确,确保模板在 classpath 或指定目录
InvalidReferenceException 变量未定义 检查模板中变量名是否与模型一致,使用 ?? 判断是否存在
MalformedTemplateNameException 模板名格式错误 检查模板名称是否包含非法字符
ParseException 模板语法错误 检查 FreeMarker 指令语法是否正确
TemplateException 模板执行错误 查看堆栈信息,定位具体行号

四、注意事项

  • 模板路径:确保模板路径正确,使用绝对路径或 classpath 路径。
  • 编码问题:模板和输出内容都应使用 UTF-8 编码。
  • 线程安全Configuration 是线程安全的,可以全局单例使用。
  • 异常处理:建议设置 TemplateExceptionHandler 来捕获模板异常。
  • 空值处理:使用 ${variable!}<#if variable??> 避免空指针异常。

五、使用技巧

  • 使用 <#include><#import>:复用模板片段,提升可维护性。
  • 定义宏(Macro)
<#macro greet name>
  <p>Hello, ${name}!</p>
</#macro>
<@greet name="Alice"/>
  • 使用内建函数
${"hello world"?cap_first} <!-- 输出 Hello world -->
${"hello world"?upper_case} <!-- 输出 HELLO WORLD -->
  • 条件判断
<#if user.isAdmin>
  <p>Welcome, Admin!</p>
</#if>
  • 集合遍历
<#list users as user>
  <p>${user.name}</p>
</#list>

六、最佳实践

  • 模板命名规范:如 user_profile.ftl,清晰表达用途。
  • 模板与逻辑分离:避免在模板中写 Java 代码。
  • 使用缓存机制:开启模板缓存提高性能。
  • 模板安全:避免在模板中执行危险操作。
  • 日志记录:记录模板渲染过程中的异常信息。
  • 模板预编译:在构建阶段预编译模板(如使用 Maven 插件)。

七、性能优化

7.1 启用模板缓存(默认已启用)

cfg.setTemplateCacheSize(100); // 设置缓存大小
cfg.setCacheStorage(new freemarker.cache.MruCacheStorage(100, 100)); // 使用 LRU 缓存策略

7.2 避免频繁创建 Configuration

  • Configuration 实例作为单例使用。

7.3 使用 StringWriter 而非 FileWriter 进行内存渲染

  • 避免频繁 IO 操作,提升性能。

7.4 使用 ?if_exists 避免模板加载失败

<#include "header.ftl" if_exists>

7.5 使用 ?default 处理空值

${user.name?default("Guest")}

八、总结

项目 内容
核心组件 Configuration, Template, DataModel
常见错误 路径错误、变量未定义、语法错误
最佳实践 单例配置、模板复用、空值处理
性能优化 模板缓存、避免重复创建、内存渲染