各位在前后端分离与不分离之间反复横跳的道友们!今天要解锁的是上古模板引擎Freemarker!这货虽然年岁已高(诞生于2003年),但在某些需要服务端渲染的场景,依然是"真香"选择!准备好用模板语法召唤动态HTML了吗? ✨
一、筑基篇:初识Freemarker
1.1 法宝祭炼(添加依赖)
xml
<!-- Spring Boot集成版 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- 原生使用版 -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.32</version>
</dependency>
1.2 基础配置(Spring Boot版)
yaml
# application.yml
spring:
freemarker:
suffix: .ftl # 模板文件后缀
template-loader-path: classpath:/templates/ # 模板存放目录
cache: false # 开发时关闭缓存
charset: UTF-8 # 必须设置编码!
content-type: text/html
二、金丹篇:模板语法大全
2.1 变量输出(灵气外放)
html
<!-- 普通变量 -->
<p>用户名:${username}</p>
<!-- 避免null值爆炸 -->
<p>余额:${balance!0}元</p> <!-- 默认值语法 -->
<!-- 日期格式化 -->
<p>注册时间:${registerTime?string("yyyy-MM-dd HH:mm")}</p>
2.2 流程控制(御剑飞行)
html
<#-- if判断 -->
<#if user.vip>
<p class="gold">尊贵的VIP用户</p>
<#else>
<p>普通用户</p>
</#if>
<#-- 遍历列表 -->
<ul>
<#list products as product>
<li>${product.name} - ¥${product.price}</li>
</#list>
</ul>
2.3 宏定义(分身术)
html
<#-- 定义宏 -->
<#macro userCard user>
<div class="card">
<h3>${user.name}</h3>
<p>${user.bio!"这个人很懒,什么都没写"}</p>
</div>
</#macro>
<#-- 使用宏 -->
<@userCard user=currentUser />
三、元婴篇:Spring Boot集成
3.1 Controller传参(灵力输送)
java
@Controller
public class UserController {
@GetMapping("/profile")
public String profile(Model model) {
model.addAttribute("username", "张无忌");
model.addAttribute("registerTime", new Date());
model.addAttribute("vip", true);
return "user/profile"; // 对应templates/user/profile.ftl
}
}
3.2 全局共享变量(宗门大阵)
java
@Configuration
public class FreemarkerConfig {
@Autowired
private freemarker.template.Configuration configuration;
@PostConstruct
public void init() throws TemplateModelException {
configuration.setSharedVariable("appName", "修仙论坛"); // 全局可用
configuration.setSharedVariable("copyright", "2023");
}
}
四、化神篇:高级技巧
4.1 自定义指令(独创神通)
java
public class RoleDirective implements TemplateDirectiveModel {
@Override
public void execute(Environment env, Map params,
TemplateModel[] loopVars,
TemplateDirectiveBody body) throws TemplateException {
String role = params.get("role").toString();
String currentRole = (String) env.getVariable("userRole");
if (role.equals(currentRole)) {
body.render(env.getOut()); // 符合条件才渲染内容
}
}
}
// 注册指令
configuration.setSharedVariable("hasRole", new RoleDirective());
模板中使用:
html
<@hasRole role="ADMIN">
<button>删除用户</button>
</@hasRole>
4.2 类型处理(灵根转换)
java
// 注册自定义日期格式化
configuration.setObjectWrapper(new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_32).build() {
@Override
public TemplateModel wrap(Object obj) throws TemplateModelException {
if (obj instanceof LocalDateTime) {
return new SimpleDate(((LocalDateTime) obj).atZone(ZoneId.systemDefault()).toInstant());
}
return super.wrap(obj);
}
});
五、大乘篇:安全与性能
5.1 防XSS攻击(护体罡气)
html
<#-- 默认输出会进行HTML转义 -->
<p>${userInput}</p>
<#-- 禁用转义(慎用!) -->
<p>${userInput?no_esc}</p>
5.2 模板缓存优化(灵气循环)
java
@Bean
public FreeMarkerConfigurationFactoryBean factoryBean() {
FreeMarkerConfigurationFactoryBean bean = new FreeMarkerConfigurationFactoryBean();
bean.setTemplateLoaderPath("classpath:/templates");
bean.setPreferFileSystemAccess(false); // 禁用文件系统监听,提升性能
bean.setDefaultEncoding("UTF-8");
return bean;
}
六、实战心法
6.1 生成静态HTML(点石成金)
java
public void generateStaticPage() throws Exception {
Template template = configuration.getTemplate("article.ftl");
Map<String, Object> data = new HashMap<>();
data.put("title", "Freemarker使用指南");
try (Writer out = new FileWriter("output.html")) {
template.process(data, out);
}
}
6.2 邮件模板应用(飞剑传书)
html
<#-- email_template.ftl -->
<html>
<body>
<h1>尊敬的${user.name}:</h1>
<p>您的新密码是:<strong>${newPassword}</strong></p>
<p>请及时<a href="${resetLink}">登录修改</a></p>
</body>
</html>
Java调用:
java
String htmlContent = FreeMarkerTemplateUtils.processTemplateIntoString(
template, model);
emailService.sendHtmlEmail(to, subject, htmlContent);
飞升指南:最佳实践
- 模板管理 :合理使用
<#include>
拆分公共部分 - 逻辑精简:复杂计算应在Java代码中完成
- 版本控制:模板文件也应纳入Git管理
- 安全第一:永远不要信任模板中的用户输入