Freemarker 完整讲解(后端 Java 模板引擎)

Freemarker 完整讲解(后端Java模板引擎)

一、Freemarker 是什么

FreeMarker 是一款Java 后端模板引擎,纯 Java 编写,不需要依赖 Servlet 容器,核心作用:

  1. 分离数据(Java 后端)视图(HTML / 文本 / 邮件 / 代码文件)

  2. 后端把对象、集合、数字、字符串传给模板,Freemarker 语法渲染输出最终文本;

  3. 常见场景:

    • 前后不分离项目渲染 HTML 页面(老管理系统、后台)

    • 动态生成邮件、短信模板

    • 自动生成代码(MyBatis Generator、代码生成器大量使用)

    • 导出动态 Word、Excel、静态页面

和 Thymeleaf/JSP 对比

  • JSP:耦合 Tomcat,编译成 Servlet,前后代码混写;

  • Freemarker:纯模板文件.ftl,无容器依赖,可离线渲染;

  • Thymeleaf:兼顾原型,Freemarker 更轻量、语法简洁,代码生成场景首选。

二、环境依赖(SpringBoot 最常用)

Maven 依赖

xml 复制代码
<!-- freemarker 核心 -->
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.32</version>
</dependency>
<!-- SpringBoot 自动装配(可选,简化配置) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

SpringBoot 基础配置 application.yml

yaml 复制代码
spring:
  freemarker:
    # 模板文件存放目录,默认 resources/templates/
    template-loader-path: classpath:/templates/
    suffix: .ftl       # 模板后缀
    charset: UTF-8
    cache: false        # 开发关闭缓存,实时刷新模板
    settings:
      number_format: 0.## # 数字格式化

模板文件统一放:resources/templates/xxx.ftl

三、核心语法 + 实战示例

基础规则

  1. ${变量名}:输出变量值;

  2. <#if>、<#else>:判断;

  3. <#list 集合 as item>:循环遍历;

  4. <#assign>:定义局部变量;

  5. ?:内置函数,处理空值、字符串、日期;

  6. # 开头是指令,$ 是取值,@ 是自定义宏。


示例 1:基础取值、空值处理、日期、数字格式化

1. Java Controller 传数据

java 复制代码
@Controller
@RequestMapping("/fm")
public class FreemarkerDemoController {

    @GetMapping("/demo1")
    public String demo1(Model model) {
        // 普通字符串
        model.addAttribute("username", "张三");
        // 数字
        model.addAttribute("price", 128.567);
        // 日期
        model.addAttribute("birthDay", new Date());
        // 空值变量
        model.addAttribute("address", null);
        // 对象
        User user = new User();
        user.setAge(26);
        user.setPhone("13800138000");
        model.addAttribute("user", user);
        return "demo1"; // 对应 templates/demo1.ftl
    }
}

// 实体类
class User {
    private Integer age;
    private String phone;
    // get/set 必须要有!Freemarker通过getter取值
}

2. templates/demo1.ftl 模板

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>基础取值示例</title>
</head>
<body>
    <h3>1. 普通变量输出</h3>
    用户名:${username}

    <h3>2. 对象属性(自动调用getAge() getPhone())</h3>
    年龄:${user.age},手机号:${user.phone}

    <h3>3. 数字格式化</h3>
    原价:${price},保留2位小数:${price?string("0.00")}

    <h3>4. 日期格式化</h3>
    原始日期:${birthDay}
    年月日:${birthDay?date("yyyy-MM-dd")}
    完整时间:${birthDay?date("yyyy-MM-dd HH:mm:ss")}

    <h3>5. 空值安全处理(重要,防止空指针报错)</h3>
    <!-- address为空时,显示默认值"未知地址" -->
    地址:${address!"未知地址"}
    <!-- 判断变量是否存在 -->
    <#if address??>
        地址存在:${address}
    <#else>
        地址为空
    </#if>
</body>
</html>

访问 /fm/demo1 自动渲染 HTML 页面。


示例 2:if 判断、逻辑分支

Controller 追加参数

java 复制代码
model.addAttribute("age", 22);
model.addAttribute("vip", true);

ftl 判断代码

html 复制代码
<h3>if 判断示例</h3>
<#if age lt 18>
    未成年,禁止访问
<#elseif age gte 18 && age lt 30>
    青年用户
<#else>
    中年用户
</#if>

<#if vip>
    <span style="color:red">VIP会员</span>
<#else>
    普通用户
</#if>

运算符:

gt大于、gte大于等于、lt小于、lte小于等于、==等于、!=不等于


示例 3:list 循环遍历(最常用,列表、表格)

Controller 传 List

java 复制代码
// 集合
List<User> userList = new ArrayList<>();
userList.add(new User(20,"13111111111"));
userList.add(new User(28,"13222222222"));
userList.add(new User(35,"13333333333"));
model.addAttribute("userList", userList);

ftl 循环渲染表格

html 复制代码
<h3>用户列表表格</h3>
<table border="1">
    <tr>
        <th>序号</th>
        <th>年龄</th>
        <th>手机号</th>
    </tr>
    <!-- 遍历集合,item单条对象,index下标从0开始 -->
    <#list userList as item>
    <tr>
        <td>${item_index + 1}</td>
        <td>${item.age}</td>
        <td>${item.phone}</td>
    </tr>
    <!-- 集合为空时显示 -->
    <#else>
    <tr>
        <td colspan="3">暂无用户数据</td>
    </tr>
    </#list>
</table>

内置变量:item_index 当前下标、item_has_next 是否有下一条。


示例 4:自定义变量 assign + 宏 macro(复用组件)

1. assign 定义临时变量

html 复制代码
<#assign pageSize = 10 />
每页条数:${pageSize}

<#assign title = "后台管理系统" />
<title>${title}</title>

2. macro 宏(封装公共组件,类似 Vue 组件)

html 复制代码
<!-- 定义分页宏,参数 currentPage total -->
<#macro page currentPage total>
    <div class="page">
        当前第${currentPage}页,总${total}页
        <a href="?page=${currentPage - 1}">上一页</a>
        <a href="?page=${currentPage + 1}">下一页</a>
    </div>
</#macro>

<!-- 调用宏,传参 -->
<@page currentPage=2 total=10 />

示例 5:纯 Java 离线渲染(不依赖 Web,代码生成场景)

不通过 SpringMVC,直接用 Freemarker API 渲染文本 / 文件,代码生成器核心用法:

java 复制代码
public class FreemarkerUtil {
    public static void main(String[] args) throws Exception {
        // 1. 配置模板加载路径
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_32);
        cfg.setDirectoryForTemplateLoading(new File("templates"));
        cfg.setDefaultEncoding("UTF-8");

        // 2. 加载模板文件
        Template template = cfg.getTemplate("code.ftl");

        // 3. 填充数据
        Map<String,Object> dataMap = new HashMap<>();
        dataMap.put("tableName", "sys_user");
        dataMap.put("className", "SysUser");

        // 4. 输出到文件(生成Java实体类)
        Writer out = new FileWriter("SysUser.java");
        template.process(dataMap, out);
        out.flush();
        out.close();
    }
}

模板code.ftl可以写 Java 代码,运行后自动生成实体类文件。

四、常见踩坑点

  1. 实体类没有 getter:Freemarker 依靠 getter 获取属性,只写 private 字段会报错;

  2. 空值不加!${address} 如果 address 为 null,直接抛异常,必须写 ${address!"默认值"}

  3. 模板缓存 :开发环境开启 spring.freemarker.cache=false,否则修改 ftl 不生效;

  4. 符号全角< #if> 空格不能乱加,指令标签不能有多余空格;

  5. 数字 / 日期格式化 :直接输出日期会显示英文时间戳,必须搭配?date格式化。

五、适用场景总结

  1. 传统服务端渲染后台管理页面;

  2. 代码生成工具(生成 Mapper、Entity、Controller);

  3. 邮件、短信、消息模板动态拼接;

  4. 批量生成静态 HTML、Word 文档;

  5. 离线文本渲染,无需 Web 容器。

相关推荐
人邮异步社区3 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大3 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai4 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说4 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json
এ慕ོ冬℘゜4 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa4 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio
一次旅行4 小时前
Python+大模型端到端自动化日报系统
开发语言·python·自动化
Zane19944 小时前
并发 vs 并行:别再傻傻分不清了,一文讲透 Java 并发编程的第一课
java·后端
噢,我明白了5 小时前
java中Excel的导入和导出(EasyExcel)
java·开发语言·excel