导读:本期聚焦于多肉创作的《如何在Spring Boot中整合Freemarker模板引擎渲染动态页面?》,敬请观看详情。在前后端分离成为主流的今天,Freemarker仍然在服务端渲染场景占有一席之地,特别是后台管理页面、邮件正文生成和HTML报告输出。Spring Boot通过starter自动装配,让整合过程比传统Spring MVC项目简单很多。本文从依赖引入、模板参数配置、Controller数据传递到页面指令用法,完整演示Freemarker渲染动态页面的步骤。文章会说明默认模板目录和后缀规则,给出用户列表遍历、空值处理、日期格式化等实用代码示例,并分享中文乱码、模板缓存、公共布局抽取等常见问题的解决办法。Freemarker的语法与JSP EL表达式相似,学习曲线平缓,代码与模板解耦彻底,适合需要长期维护的服务端模板项目。读者可以直接套用示例代码,快速在项目中跑通Freemarker。

Spring Boot对Freemarker的整合非常直接,引入对应starter后,自动配置模块会完成视图解析器的初始化,开发者只需要关注模板目录和Controller返回的视图名称。Freemarker模板文件使用类似HTML的语法,通过指令和插值表达式完成数据渲染,在后端管理页面、邮件正文生成等场景仍然非常实用。本文以用户列表页为例,从零跑通整个渲染流程,并说明几个容易踩坑的配置项。

如何在Spring Boot中整合Freemarker模板引擎渲染动态页面?

一、添加依赖与配置模板参数

在Spring Boot项目中整合Freemarker,最方便的方式是使用官方提供的starter。Maven工程在pom.xml中加入以下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

如果使用Gradle,则在build.gradle中增加implementation 'org.springframework.boot:spring-boot-starter-freemarker'。依赖引入后,Spring Boot的FreemarkerAutoConfiguration会自动创建FreeMarkerConfigurer和FreeMarkerViewResolver,默认从classpath:/templates/目录加载模板,默认后缀是.ftlh。注意早期版本的Spring Boot默认后缀是.ftl,升级项目时最好显式指定后缀,避免模板找不到。

在application.properties或application.yml中,通常只需要配置少量参数。例如开发阶段关闭缓存,保证修改模板后刷新页面立即生效;同时统一字符集,避免中文乱码:

spring.freemarker.cache=false
spring.freemarker.charset=UTF-8
spring.freemarker.content-type=text/html;charset=UTF-8
spring.freemarker.suffix=.ftl
spring.freemarker.template-loader-path=classpath:/templates/

这里显式把后缀设置为.ftl,如果你更喜欢Spring Boot 2之后的默认后缀.ftlh,直接使用即可,但Controller返回值要保持一致。template-loader-path也可以配置为其他目录,比如classpath:/views/,但生产环境一般保持默认目录结构更清晰。

二、Controller传递数据与模板文件编写

动态页面离不开后端数据。Freemarker本身不关心数据来自数据库还是远程接口,它只负责把模型中的数据渲染到模板中。下面的Controller返回一个视图名,同时把用户列表放进Model:

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;

@Controller
@RequestMapping("/users")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/list")
    public String listUsers(Model model) {
        List<User> users = userService.findAll();
        model.addAttribute("users", users);
        model.addAttribute("title", "用户列表");
        return "user/list";
    }
}

这里返回的字符串user/list会交给视图解析器,最终定位到src/main/resources/templates/user/list.ftl。如果模板放在子目录,注意返回值不要带后缀,也不要以斜杠开头。

模板文件本质上是一个HTML文件,只是嵌入了Freemarker的插值和指令。下面是一个简单的列表页,遍历users集合并输出表格:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>${title}</title>
</head>
<body>
    <h1>${title}</h1>
    <table border="1" cellpadding="8">
        <tr>
            <th>ID</th>
            <th>姓名</th>
            <th>注册时间</th>
        </tr>
        <#list users as user>
        <tr>
            <td>${user.id}</td>
            <td>${user.name}</td>
            <td>${user.createdAt?string("yyyy-MM-dd HH:mm")}</td>
        </tr>
        </#list>
    </table>
</body>
</html>

在模板中,${...}表示插值表达式,Freemarker会读取User对象的id和name属性。日期类型默认输出可能不符合中文习惯,所以使用内置函数?string指定格式。<#list>指令负责遍历,需要用</#list>闭合。模板语法与HTML标签混写时,建议保持缩进一致,便于维护。

三、核心指令与空值处理

除了遍历,Freemarker还支持条件判断、变量定义、宏和包含。条件判断使用<#if>,可以结合??判断变量是否存在。比如当用户列表为空时显示提示:

<#if users?? && users?size > 0>
    <table border="1">
        <tr><th>姓名</th></tr>
        <#list users as user>
        <tr><td>${user.name}</td></tr>
        </#list>
    </table>
<#else>
    <p>暂无用户数据</p>
</#if>

这里users??先判断users是否存在于模型,users?size > 0表示集合大小大于0。注意模板中的大于号需要写成&gt;,否则会破坏指令解析。也可以使用users?has_content一次性判断非空。

空值处理是Freemarker项目中经常遇到的问题。如果模型里缺少某个属性,直接用${user.address.city}可能会抛出InvalidReferenceException。推荐在不确定的取值处添加默认值,语法是${user.address.city!"未知城市"}。如果只想在null时不输出任何内容,可以写成${user.phone!}。对于嵌套对象,还可以使用${(user.address.city)!}进行整体安全访问。

日期和数字格式化也很常用。日期格式可以通过?string指定,数字保留小数位使用?string("0.00"),金额场景尤其常见。如果需要调用Java静态方法,Freemarker不直接支持,建议在Controller中预处理数据,把格式化结果放入模型,避免模板逻辑过于复杂。

四、抽取公共布局与优化建议

后台管理系统的页面通常有统一的顶部导航和侧边栏,如果每个模板都复制一份HTML结构,后期修改会非常麻烦。Freemarker支持<#include>指令,可以把公共部分拆成独立文件。例如创建common/header.ftl和common/footer.ftl,在业务模板中使用:

<#include "common/header.ftl">
<main>
    <h1>${title}</h1>
    <p>页面主体内容</p>
</main>
<#include "common/footer.ftl">

如果公共区域需要传递参数,可以使用<#macro>定义宏,像函数一样复用。宏可以放在单独的.ftl文件中,通过<#import>引入,调用时传入页面标题、激活菜单等参数,比简单include更灵活。不过宏会让模板逻辑增加,适合布局比较稳定的项目。

开发过程中,模板缓存建议关闭,修改后直接刷新浏览器查看。生产环境一定要开启缓存,减少每次请求的模板解析开销。中文乱码问题通常源于模板文件本身不是UTF-8编码,或者响应头content-type没有指定charset。确保application.properties中配置了spring.freemarker.charset=UTF-8,同时IDE里模板文件保存为UTF-8无BOM格式,基本可以避免乱码。

与Thymeleaf相比,Freemarker语法更接近JSP时代的EL表达式,学习成本低,而且模板文件可以被非Java系统复用,比如Python或Node.js的Freemarker实现。如果你的项目需要生成离线HTML报告或邮件正文,Freemarker是轻量的选择;如果页面与前端框架交互频繁,建议只把Freemarker用于初始页面容器,数据通过接口异步加载。

最后,所有模板文件的路径和Controller返回值要严格对应。遇到404或找不到模板错误时,先检查返回的视图名是否带后缀、模板是否放在classpath:/templates/目录下、spring.freemarker.suffix配置是否与文件后缀一致。把这些基础项排查清楚,整合流程通常不会出现复杂问题。

Spring BootFreemarker模板引擎修改时间:2026-09-23 05:13:02

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0923/60769.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。