1. FreeMarker模版引擎基础入门第一次接触FreeMarker时我被它的简洁设计所吸引。作为一个老牌Java模板引擎它不像某些框架那样需要复杂的配置却能解决动态内容生成的核心痛点。想象一下你正在开发一个企业官网每次有新产品上线都需要手动修改HTML文件这得多麻烦FreeMarker就是为解决这类问题而生的。FreeMarker的工作原理很像我们日常使用的邮件合并功能。你准备好一个模板比如邮件正文然后填入不同的数据收件人姓名、产品信息等最后批量生成个性化内容。只不过FreeMarker把这个过程搬到了代码层面让程序能自动化完成。与同类产品相比FreeMarker有几个显著优势轻量级不依赖Spring等框架纯Java环境就能运行学习曲线平缓基础语法半小时就能掌握性能出色经过Apache多年优化处理速度有保障我建议初学者从官方文档入手虽然英文看起来有些吃力但示例代码非常丰富。实在看不下去的话跟着我接下来的实战走一遍保准你能快速上手。2. 核心语法深度解析2.1 插值动态内容的基石插值是FreeMarker最基础也最常用的功能。它的语法简单到令人发指 - 只需要用${}包裹变量名就行。比如h1欢迎${username}/h1但别小看这个简单语法它支持各种表达式运算。比如我在电商项目中经常用到的价格计算总价${item.price * item.quantity}不过有个坑我得提醒你FreeMarker对null值特别敏感。如果username为null整个模板处理会直接中断。解决方法有两种设置默认值${username!匿名用户}提前判空#if username??${username}/#if2.2 条件分支让模板更智能条件判断能让你的模板根据不同数据展示不同内容。最近我做的一个后台管理系统就用到了这个特性#if user.role admin button classdanger删除用户/button #elseif user.role editor button编辑内容/button #else button disabled无权限/button /#if特别实用的一个技巧是类型判断。有次我遇到个bug数据库返回的数字有时是Integer有时是String导致页面显示异常。后来用这个方案解决#if item.id?is_string ID是字符串类型 #else ID是数字类型 /#if2.3 循环遍历列表渲染利器处理列表数据是Web开发的日常FreeMarker的list指令用起来相当顺手。比如渲染产品目录ul #list products as product li h3${product.name}/h3 p价格${product.price?string.currency}/p /li /#list /ul循环内还可以通过product_index获取当前索引这在生成表格时特别有用table #list employees as emp tr td${emp_index 1}/td td${emp.name}/td /tr /#list /table3. 高级特性实战应用3.1 宏定义模板中的函数宏是FreeMarker最强大的功能之一它相当于模板中的函数。我在多个项目中都创建了公共宏库来保持UI一致性。比如这个按钮宏#macro button color size href a href${href} classbtn btn-${color} btn-${size} #nested /a /#macro使用时就像调用函数一样button colorprimary sizelg href/login 立即登录 /button更厉害的是宏支持嵌套内容。有次我需要实现一个可折叠面板用宏轻松搞定#macro collapse title div classpanel h3${title}/h3 div classcontent #nested /div /div /#macro collapse title高级选项 form.../form /collapse3.2 内建函数数据处理神器FreeMarker提供了丰富的内建函数能大大简化模板逻辑。分享几个我常用的日期格式化在报表系统中必不可少${order.createTime?string(yyyy-MM-dd HH:mm)}字符串处理也很常见${description?truncate(100)} !-- 截断长文本 -- ${keywords?split(,)?join( | )} !-- 转换分隔符 --处理JSON数据时这个技巧很实用#assign config configJson?eval / ${config.themeColor}4. 企业官网实战案例4.1 项目结构与配置让我们用FreeMarker实现一个真实的企业官网。项目结构如下src/ ├── main/ │ ├── java/ │ ├── resources/ │ │ └── templates/ │ │ ├── layout.ftl │ │ ├── index.ftl │ │ └── news/ │ │ └── list.ftl │ └── webapp/ └── test/配置FreeMarker只需几行代码Configuration cfg new Configuration(Configuration.VERSION_2_3_32); cfg.setClassForTemplateLoading(getClass(), /templates); cfg.setDefaultEncoding(UTF-8); cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);4.2 页面布局设计使用宏实现布局复用是业界最佳实践。layout.ftl定义整体框架#macro main title !DOCTYPE html html head title${title} | 企业官网/title link relstylesheet href/css/style.css /head body header #include nav.ftl /header main #nested /main footer #include footer.ftl /footer /body /html /#macro具体页面继承这个布局#import layout.ftl as layout layout.main title首页 h1最新产品/h1 #-- 页面特有内容 -- /layout.main4.3 动态内容生成新闻列表页展示如何结合后端数据#list newsList as news article h3${news.title}/h3 time${news.publishTime?string(yyyy-MM-dd)}/time p${news.summary}/p a href/news/detail/${news.id}阅读全文/a /article /#list分页组件实现div classpagination #if pageInfo.hasPrevious a href?page${pageInfo.previousPage}上一页/a /#if #list pageInfo.navigatePages as num #if num pageInfo.pageNum span classcurrent${num}/span #else a href?page${num}${num}/a /#if /#list #if pageInfo.hasNext a href?page${pageInfo.nextPage}下一页/a /#if /div5. 性能优化与最佳实践5.1 模板缓存配置生产环境一定要启用模板缓存cfg.setCacheStorage(new StrongCacheStorage()); cfg.setTemplateUpdateDelay(3600); // 1小时更新检查我遇到过因为没配置缓存导致QPS上不去的情况。设置后性能提升了8倍多。5.2 错误处理技巧建议自定义错误页面cfg.setTemplateExceptionHandler((ex, env, out) - { out.write(系统繁忙请稍后再试); log.error(模板处理错误, ex); });对于可能为空的字段统一处理更安全${(user.birthday!暂无)?string(yyyy-MM-dd)}5.3 调试技巧分享开发时启用这个配置能看到详细错误cfg.setLogTemplateExceptions(false); cfg.setWrapUncheckedExceptions(true);我常用的调试小技巧#-- 打印变量类型 -- ${someVar?class.simpleName} #-- 输出完整对象 -- pre ${dataModel?keys?join(, )} /pre6. 常见问题解决方案6.1 数字格式化问题中文环境下数字会显示为1,234要取消千分位分隔符cfg.setNumberFormat(0.##);金融项目需要保留两位小数${amount?string(0.00)}6.2 包含文件路径问题包含子模板时建议使用绝对路径#include /common/header.ftl我曾经踩过的坑相对路径在不同目录下引用会失效。6.3 特殊字符转义防止XSS攻击务必转义HTML${userInput?html}JSON数据需要双重转义cfg.setOutputFormat(HTMLOutputFormat.INSTANCE);7. 扩展应用场景7.1 生成静态化页面我们使用FreeMarker批量生成静态产品页Template temp cfg.getTemplate(product.ftl); try (Writer out new FileWriter(output.html)) { temp.process(dataModel, out); }7.2 邮件模板系统用FreeMarker实现多语言邮件模板#if locale zh 尊敬的${name}您的订单已发货 #else Dear ${name}, your order has shipped /#if7.3 代码生成工具我开发过基于FreeMarker的代码生成器MapString, Object model new HashMap(); model.put(className, User); model.put(fields, fieldList); Template temp cfg.getTemplate(entity.java.ftl); StringWriter result new StringWriter(); temp.process(model, result);8. 与其他技术整合8.1 Spring Boot集成Spring Boot自动配置让集成更简单spring.freemarker.template-loader-pathclasspath:/templates spring.freemarker.suffix.ftl spring.freemarker.cachetrueController返回视图名即可GetMapping(/) public String index(Model model) { model.addAttribute(message, Hello); return index; }8.2 与前端框架配合FreeMarker可以和Vue.js完美配合div idapp !-- Vue接管这部分 -- {{ message }} /div script var appData { message: ${serverMessage?js_string} }; /script8.3 在微服务中的应用在API网关用FreeMarker转换响应数据Template temp cfg.getTemplate(api-response.ftl); String result FreeMarkerTemplateUtils.processTemplateIntoString( temp, responseData);