首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

FreeMarker 程序開發實戰:從模板引擎到通用代碼生成器的核心原理與最佳實踐

     編輯:Java 綜合教學

FreeMarker 核心定位

FreeMarker 是一個基於模板的文本生成引擎,最初為 Web 視圖層設計(替代 JSP),憑藉強大的宏系統、指令體系、數據模型抽象,早已超越 Web 領域,廣泛應用於:

  • 代碼生成器(MyBatis Mapper、Entity、Controller 骨架)
  • 配置文件生成
  • 靜態頁面預渲染
  • 郵件/報表/文檔模板化輸出

本文系統梳理 FreeMarker 核心概念、配置體系、模板語法進階與非 Web 場景集成方案。

核心架構三大支柱

  1. Configuration:全局單例,持有模板加載器、緩存策略、共享變量、輸出編碼、數據模型包裝器等應用級設置。
  2. Template:預解析後的模板對象,線程安全,可重復 process(dataModel, writer)。
  3. Data Model:鍵值映射(Map/POJO/自定義 TemplateModel),為模板提供動態數據。

快速上手:最小化集成示例

// 1. 構建 Configuration(僅在應用啟動時執行一次)
Configuration cfg = new Configuration(Configuration.VERSION_2_3_32);
cfg.setDirectoryForTemplateLoading(new File("/path/to/templates"));
cfg.setDefaultEncoding("UTF-8");
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
cfg.setLogTemplateExceptions(false);
cfg.setWrapUncheckedExceptions(true);
cfg.setSharedVariable("siteName", "MyApp");  // 全局共享變量

// 2. 獲取/緩存 Template
Template template = cfg.getTemplate("user/profile.ftl");

// 3. 準備數據模型
Map model = new HashMap<>();
model.put("user", userService.getById(1L));
model.put("roles", roleService.listByUserId(1L));

// 4. 渲染輸出
try (StringWriter out = new StringWriter()) {
    template.process(model, out);
    String html = out.toString();  // 直接獲取生成內容
    // 寫入文件、返回響應、發送郵件...
}

模板語法精華

1. 變量與表達式

<#-- 基礎輸出 -->
${user.name?html}           <#-- HTML 轉義,防 XSS -->
${user.age!0}               <#-- 缺失值默認 0 -->
${user.tags?join(", ")}     <#-- 序列轉字符串 -->
${(user.balance/100)?string("0.00")}  <#-- 數字格式化 -->
${user.createTime?datetime?string("yyyy-MM-dd HH:mm")}  <#-- 日期格式化 -->

2. 流程控制

<#list users as u>
    <#if u_index == 0>
    <#if u_has_next><#else>
${u.name} <#if u.vip>VIP<#else>普通
<#switch status> <#case 1>待審核<#break> <#case 2>通過<#break> <#default>未知

3. 宏與函數:構建可復用 UI 組件庫

<#-- 定義宏 -->
<#macro btn type="primary" size="md" label="按鈕" onclick="">



<#-- 調用宏 -->
<@btn type="success" label="提交" onclick="submitForm()"/>
<@btn label="取消" onclick="closeModal()"/>  <#-- 使用默認值 -->

4. 包含與布局

<#include "common/header.ftl">  <#-- 靜態包含,編譯期合併 -->
<@layout title="用戶詳情">
    <#-- 動態填充區塊 -->
    <#nested>

進階配置與優化

模板加載器選型

加載器適用場景特點
FileTemplateLoader開發/外部化模板熱加載友好,需文件系統權限
ClassTemplateLoader打包進 JAR 的內部模板加載快、不可熱更、適合生產
WebappTemplateLoader傳統 WAR 部署基於 ServletContext 獲取資源
MultiTemplateLoader多源混合鏈式委託,優先級可控

緩存策略

  • 強緩存:cfg.setTemplateUpdateDelayMilliseconds(-1) — 生產環境推薦,重啟應用生效。
  • 間隔檢查:cfg.setTemplateUpdateDelayMilliseconds(5000) — 開發環境,5秒檢查一次文件變更。
  • 自定義緩存:實現 TemplateCache 接口,接入 Redis/分布式緩存。

對象包裝器

默認 DefaultObjectWrapper 將 POJO/Map/List 自動包裝為 TemplateHashModel/TemplateSequenceModel。可通過 cfg.setObjectWrapper(new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_32).build()) 微調:

  • 禁止暴露危險方法:wrapper.setExposeFields(false)
  • 自定義枚舉/日期/集合渲染邏輯

非 Web 場景實戰:代碼生成器架構

public class CodeGenerator {
    private final Configuration cfg;

    public CodeGenerator(String templateRoot) throws IOException {
        cfg = new Configuration(Configuration.VERSION_2_3_32);
        cfg.setDirectoryForTemplateLoading(new File(templateRoot));
        cfg.setDefaultEncoding("UTF-8");
    }

    public void generateEntity(TableMeta meta, File outputDir) throws Exception {
        Template tpl = cfg.getTemplate("entity.ftl");
        Map model = Map.of(
            "table", meta,
            "columns", meta.getColumns(),
            "pkg", "com.demo.entity",
            "author", System.getProperty("user.name"),
            "date", LocalDate.now()
        );
        File outFile = new File(outputDir, meta.getClassName() + ".java");
        try (Writer w = new OutputStreamWriter(new FileOutputStream(outFile), "UTF-8")) {
            tpl.process(model, w);
        }
    }
}

配合 entity.ftl 宏定義 getter/setter、Builder、toString 等樣板代碼,可在秒級生成數百個實體類,並保證風格統一。

常見坑點避雷指南

  1. 空值處理:務必使用 !default 或 ?? 判空,否則拋 TemplateModelException。
  2. 布爾值轉字符串:${flag?c} 輸出 "true/false",${flag?string("是","否")} 自定義標籤。
  3. 數字去科學計數法:大數字默認輸出 1.23E7,用 ?string("0") 或 ?c 強制定點。
  4. 循環變量作用域:u_index、u_has_next 僅在 #list 內部可用。
  5. 輸出編碼一致性:模板文件、Configuration、OutputStreamWriter 必須統一 UTF-8。

總結

FreeMarker 以模板即代碼、數據驅動視圖的極簡哲學,成為 Java 生態最通用的文本生成引擎。掌握其核心 API、指令體系、宏復用機制與配置調優,即可從 Web 視圖渲染延伸至代碼生成、文檔自動化、配置管理等諸多場景,實現「寫一次模板,到處生成代碼」的工程效能躍升。

官方文檔:FreeMarker Manual | 版本基線:2.3.32 (LTS)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved