FreeMarker 是一个强大的 Java 模板引擎,广泛用于生成 HTML、XML、JSON 或纯文本。它本身是一个独立的 Java 库,可以与任何 Java 应用集成。Maven 和 Gradle 是 Java 生态中最主流的构建自动化工具,它们负责管理项目依赖、编译、测试、打包和部署。将 FreeMarker 与 Maven 或 Gradle 集成,核心在于通过构建工具的依赖管理功能,将 FreeMarker 库及其传递依赖自动下载并添加到项目的类路径(classpath)中


1. 核心概念

  • FreeMarker 库 (freemarker): FreeMarker 模板引擎的核心 JAR 包,包含 freemarker.template.Configuration, freemarker.template.Template 等核心类。这是集成的基础。
  • 构建工具 (Build Tool): 自动化项目构建过程的工具。Maven 和 Gradle 是其代表。
  • 依赖管理 (Dependency Management): 构建工具的核心功能。通过在项目配置文件(pom.xml for Maven, build.gradle for Gradle)中声明对 FreeMarker 的依赖,构建工具会自动从中央仓库(如 Maven Central)下载 FreeMarker JAR 及其所有必需的传递依赖(transitive dependencies)到本地仓库,并在编译和运行时将其加入 classpath。
  • 中央仓库 (Central Repository): 存储开源 Java 库(JAR 包)的公共服务器。Maven Central 是最常用的。构建工具默认从这里下载依赖。
  • pom.xml (Maven Project Object Model): Maven 项目的配置文件,使用 XML 格式。<dependencies> 部分用于声明项目依赖。
  • build.gradle (Gradle Build Script): Gradle 项目的配置文件,使用 Groovy 或 Kotlin DSL (Kotlin DSL 更现代)。dependencies 代码块用于声明项目依赖。
  • 依赖范围 (Scope - Maven) / 配置 (Configuration - Gradle): 定义依赖在项目生命周期中的使用范围。
    • compile (Maven) / implementation (Gradle): 依赖在编译、测试和运行时都可用。这是添加 FreeMarker 的标准范围
    • provided (Maven) / compileOnly (Gradle): 依赖在编译和测试时需要,但不会被打包进最终的 JAR/WAR 文件。适用于由运行时环境(如 Servlet 容器)提供的库(如 servlet-api)。
    • test (Maven) / testImplementation (Gradle): 依赖仅在运行测试时需要(如 JUnit)。
  • 版本管理: 依赖声明中必须指定 FreeMarker 的版本号(如 2.3.32)。构建工具确保使用指定的版本。
  • 传递依赖 (Transitive Dependencies): FreeMarker 本身可能依赖于其他库(如 commons-io)。构建工具会自动解析并下载这些依赖,简化了依赖管理。
  • 本地仓库 (Local Repository): 构建工具在用户主目录下(如 ~/.m2/repository for Maven, ~/.gradle/caches for Gradle)缓存下载的依赖,避免重复下载。

2. 操作步骤 (非常详细)

以下是将 FreeMarker 集成到 Maven 和 Gradle 项目中的详细步骤。假设你已经创建了一个基本的 Java 项目。

通用前提

  1. 安装构建工具:
  2. 创建项目结构: 确保项目有标准的目录结构。
    • src/main/java - Java 源代码
    • src/main/resources - 资源文件(如 FreeMarker 模板 .ftl
    • src/test/java - 测试代码
    • src/test/resources - 测试资源

使用 Maven 集成 FreeMarker

步骤 1: 创建或定位 pom.xml

  • 在项目根目录下创建一个名为 pom.xml 的文件,或者编辑已存在的 pom.xml

步骤 2: 编写 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- 项目基本信息 -->
    <groupId>com.example</groupId>
    <artifactId>freemarker-maven-demo</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging> <!-- 或 war -->

    <!-- 属性定义 (可选,便于版本管理) -->
    <properties>
        <maven.compiler.source>17</maven.compiler.source> <!-- 或你的 Java 版本 -->
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!-- 定义 FreeMarker 版本变量 -->
        <freemarker.version>2.3.32</freemarker.version>
    </properties>

    <!-- 依赖声明 -->
    <dependencies>
        <!-- 核心: 添加 FreeMarker 依赖 -->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>${freemarker.version}</version> <!-- 使用属性变量 -->
            <!-- scope 默认是 compile, 可以省略 -->
            <!-- <scope>compile</scope> -->
        </dependency>

        <!-- 示例: 添加 SLF4J API (用于日志,FreeMarker 可使用) -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>2.0.9</version>
        </dependency>
        <!-- 示例: 添加 SLF4J Simple 实现 (简单日志输出) -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>2.0.9</version>
        </dependency>

        <!-- 测试依赖 -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.10.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <!-- 构建配置 (可选) -->
    <build>
        <plugins>
            <!-- 编译器插件 (确保使用正确的 Java 版本) -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                </configuration>
            </plugin>

            <!-- 打包插件 (可选,创建可执行 JAR) -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.5.0</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>com.example.App</mainClass> <!-- 替换为你的主类 -->
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

步骤 3: 下载依赖

  • 方法 1 (命令行): 在项目根目录(pom.xml 所在目录)打开终端,执行:
    mvn clean compile
    
    clean 清理旧编译文件,compile 编译源代码,此过程会自动下载 pom.xml 中声明的所有 compiletest 范围的依赖(包括 FreeMarker 及其传递依赖)到本地 Maven 仓库(~/.m2/repository),并编译项目。
  • 方法 2 (IDE): 如果使用 IntelliJ IDEA 或 Eclipse,打开项目后,IDE 通常会自动检测 pom.xml 的更改并提示导入或刷新项目。点击 "Import Changes" 或 "Reload Project" 即可。

步骤 4: 使用 FreeMarker (代码示例)

  1. 创建模板:src/main/resources/templates/ 目录下创建一个 FreeMarker 模板文件,例如 hello.ftl:
    <!-- src/main/resources/templates/hello.ftl -->
    <#-- This is a FreeMarker template -->
    <h1>Hello, ${name}!</h1>
    <p>Welcome to FreeMarker with Maven.</p>
    <ul>
    <#list items as item>
        <li>${item}</li>
    </#list>
    </ul>
    
  2. 编写 Java 代码:src/main/java/com/example/ 下创建主类 App.java:
    // src/main/java/com/example/App.java
    package com.example;
    
    import freemarker.template.Configuration;
    import freemarker.template.Template;
    import freemarker.template.TemplateException;
    import freemarker.template.Version;
    
    import java.io.*;
    import java.util.HashMap;
    import java.util.Map;
    
    public class App {
        public static void main(String[] args) {
            try {
                // 1. 创建 FreeMarker Configuration 实例
                // 使用新 API (Recommended)
                Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
    
                // 2. 设置模板加载路径
                // 从 classpath 加载模板
                cfg.setClassForTemplateLoading(App.class, "/templates");
    
                // 或者从文件系统加载 (指定绝对或相对路径)
                // cfg.setDirectoryForTemplateLoading(new File("/path/to/templates"));
    
                // 3. 获取模板对象
                Template template = cfg.getTemplate("hello.ftl"); // 文件名
    
                // 4. 创建数据模型 (Model)
                Map<String, Object> dataModel = new HashMap<>();
                dataModel.put("name", "World");
                dataModel.put("items", new String[]{"Apple", "Banana", "Cherry"});
    
                // 5. 合并模板和数据模型,输出到 Writer
                // 输出到控制台
                Writer out = new OutputStreamWriter(System.out);
                template.process(dataModel, out);
                out.flush();
    
                // 或者输出到文件
                // try (Writer fileWriter = new FileWriter(new File("output.html"))) {
                //     template.process(dataModel, fileWriter);
                // }
    
            } catch (IOException | TemplateException e) {
                e.printStackTrace();
            }
        }
    }
    
  3. 编译和运行:
    • 命令行:
      # 编译
      mvn compile
      # 运行 (使用 exec:java 插件或先打包)
      mvn exec:java -Dexec.mainClass="com.example.App"
      # 或者先打包成 JAR (如果配置了 maven-shade-plugin)
      # mvn package
      # java -jar target/freemarker-maven-demo-1.0.0-SNAPSHOT.jar
      
    • IDE: 在 IDE 中直接运行 App 类的 main 方法。

使用 Gradle 集成 FreeMarker

步骤 1: 初始化 Gradle 项目 (可选)

  • 在项目根目录打开终端,执行:
    gradle init
    
    按提示选择项目类型(如 basic)、DSL(推荐 KotlinGroovy)、项目名称等。这会生成 build.gradle (或 build.gradle.kts) 和 settings.gradle (或 settings.gradle.kts)。

步骤 2: 编写 build.gradle (Groovy DSL)

// build.gradle (Groovy DSL)
plugins {
    id 'java' // 应用 Java 插件,提供编译、测试等任务
    id 'application' // 可选,用于创建可运行的应用
}

// 项目基本信息
group = 'com.example'
version = '1.0.0-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_17 // 或 JavaVersion.VERSION_11 等

// 定义属性 (便于版本管理)
ext {
    freemarkerVersion = '2.3.32'
    slf4jVersion = '2.0.9'
    junitVersion = '5.10.0'
}

repositories {
    // 声明依赖仓库
    mavenCentral() // 最常用的中央仓库
    // jcenter() // (已归档,不推荐新项目使用)
    // maven { url 'https://repo.spring.io/milestone' } // 其他仓库
}

dependencies {
    // 核心: 添加 FreeMarker 依赖
    implementation "org.freemarker:freemarker:${freemarkerVersion}"

    // 示例: 添加 SLF4J
    implementation "org.slf4j:slf4j-api:${slf4jVersion}"
    implementation "org.slf4j:slf4j-simple:${slf4jVersion}"

    // 测试依赖
    testImplementation "org.junit.jupiter:junit-jupiter:${junitVersion}"
}

// 配置 application 插件 (可选)
application {
    mainClass = 'com.example.App' // 替换为你的主类全名
}

// 配置编译任务 (可选)
compileJava {
    options.encoding = 'UTF-8' // 设置源码编码
}

步骤 2 (替代): 编写 build.gradle.kts (Kotlin DSL - 推荐)

// build.gradle.kts (Kotlin DSL)
plugins {
    java
    application // 可选
}

group = "com.example"
version = "1.0.0-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_17

val freemarkerVersion: String by project
val slf4jVersion: String by project
val junitVersion: String by project

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.freemarker:freemarker:$freemarkerVersion")
    implementation("org.slf4j:slf4j-api:$slf4jVersion")
    implementation("org.slf4j:slf4j-simple:$slf4jVersion")
    testImplementation("org.junit.jupiter:junit-jupiter:$junitVersion")
}

application {
    mainClass.set("com.example.App") // 替换为你的主类
}

// (可选) 通过 gradle.properties 文件管理版本
// 创建 gradle.properties 文件 (与 build.gradle.kts 同级)
// freemarkerVersion=2.3.32
// slf4jVersion=2.0.9
// junitVersion=5.10.0

步骤 3: 下载依赖

  • 方法 1 (命令行): 在项目根目录执行:
    # 使用 Gradle Wrapper (推荐,项目自带)
    ./gradlew build # 或 ./gradlew compileJava
    # 或使用全局 Gradle
    gradle build
    
    build 任务会执行编译、测试等,compileJava 仅编译。执行时会自动下载所有声明的依赖到本地 Gradle 缓存(~/.gradle/caches/modules-2/files-2.1/)。
  • 方法 2 (IDE): 打开项目后,IDE (IntelliJ IDEA, Eclipse with Buildship) 通常会自动识别 build.gradle 并提示同步。点击 "Sync Now" 或 "Reload Gradle Project"。

步骤 4: 使用 FreeMarker

  • 模板文件 (src/main/resources/templates/hello.ftl) 和 Java 代码 (src/main/java/com/example/App.java) 与 Maven 示例完全相同
  • 编译和运行:
    • 命令行 (Wrapper):
      # 编译
      ./gradlew compileJava
      # 运行 (使用 application 插件)
      ./gradlew run
      # 或者运行特定任务
      # ./gradlew classes # 编译
      # java -cp build/classes/java/main:build/resources/main:$(./gradlew dependencies --configuration runtimeClasspath --console=plain | grep -o '.*\.jar' | tr '\n' ':') com.example.App
      
    • 命令行 (全局 Gradle):
      gradle run
      
    • IDE: 直接运行 App 类的 main 方法。

3. 常见错误

  1. ClassNotFoundException / NoClassDefFoundError:

    • 原因: FreeMarker JAR 未正确下载或未添加到 classpath。
    • 解决:
      • 检查 pom.xml / build.gradle 中的 groupId, artifactId, version 是否拼写正确。
      • 执行 mvn dependency:tree (Maven) 或 ./gradlew dependencies (Gradle) 查看依赖树,确认 freemarker 是否在 compile / implementation 配置中。
      • 检查网络连接,确保能访问 Maven Central。尝试手动删除本地仓库中对应的 FreeMarker 目录(~/.m2/repository/org/freemarker/~/.gradle/caches/modules-2/files-2.1/org.freemarker/),然后重新执行构建命令强制下载。
      • 在 IDE 中刷新/重新导入项目。
  2. Could not resolve dependencies / Could not find ...:

    • 原因: 指定的 FreeMarker 版本在仓库中不存在,或仓库 URL 配置错误/网络问题。
    • 解决:
  3. 编译错误 (package freemarker.template does not exist):

    • 原因: IDE 未正确识别构建文件或依赖未下载。
    • 解决: 强制刷新 Maven/Gradle 项目。在 IntelliJ IDEA 中,点击 Maven/Gradle 工具窗口的刷新按钮。在 Eclipse 中,右键项目 -> Maven -> Update Project / Gradle -> Refresh Gradle Project。
  4. 运行时找不到模板 (Template not found):

    • 原因: cfg.setClassForTemplateLoading()cfg.setDirectoryForTemplateLoading() 路径设置错误。模板文件未正确打包到 JAR/WAR 中。
    • 解决:
      • 确认模板文件在 src/main/resources 目录下,且路径与 setClassForTemplateLoading 的第二个参数(如 "/templates")匹配。
      • 使用 cfg.getTemplate("templates/hello.ftl") 如果模板在 resources/templates/ 下。
      • 打包后检查 JAR/WAR 文件内容,确认 templates/ 目录和 .ftl 文件存在。
  5. Gradle DSL 语法错误:

    • 原因: Groovy/Kotlin 语法错误(如缺少分号、括号不匹配、字符串引号问题)。
    • 解决: 仔细检查 build.gradle 语法。使用 IDE 的 Gradle 支持进行语法检查。参考官方文档。

4. 注意事项

  1. 版本选择: 选择稳定且适合你项目需求的 FreeMarker 版本。查看 FreeMarker 官网Maven Central 获取最新稳定版。注意版本号(如 2.3.32)与 API 兼容性。
  2. 依赖范围/配置: 确保 FreeMarker 依赖使用正确的范围(Maven compile/implementation)或配置(Gradle implementation),使其在编译和运行时都可用。避免误用 provided/compileOnly,除非你确定运行环境会提供它。
  3. 传递依赖: 理解传递依赖的概念。freemarker 依赖可能引入 commons-io 等。构建工具会自动处理,但有时可能需要排除冲突的传递依赖(使用 <exclusions> in Maven, exclude in Gradle)。
  4. 文件编码: 确保 .ftl 模板文件和 Java 源文件都使用 UTF-8 编码,避免乱码。在构建脚本或 IDE 中设置编码。
  5. src/main/resources: FreeMarker 模板通常放在 src/main/resources 下,这样它们会被打包进最终的 JAR/WAR 文件,可以通过 classpath 访问。
  6. 构建工具选择: Maven 配置更标准化(XML),Gradle 脚本更灵活强大(Groovy/Kotlin DSL),性能通常更好。根据团队习惯和项目复杂度选择。
  7. Gradle Wrapper: 强烈推荐在项目中包含 Gradle Wrapper (gradlew, gradlew.bat, gradle/wrapper/),确保所有开发者使用相同的 Gradle 版本,避免版本冲突。

5. 使用技巧

  1. 使用属性/变量管理版本:pom.xml<properties> 或 Gradle 的 ext/gradle.properties 中定义版本号变量,便于统一管理和升级。
  2. 利用 IDE 支持: IntelliJ IDEA, Eclipse 等主流 IDE 对 Maven 和 Gradle 都有 excellent 支持,提供依赖搜索、自动补全、错误提示、项目刷新等功能。
  3. 查看依赖树: 使用 mvn dependency:tree (Maven) 或 ./gradlew dependencies (Gradle) 分析依赖关系,排查冲突或了解传递依赖。
  4. 排除传递依赖: 当传递依赖引入了冲突的库版本时,可以显式排除。
    • Maven:
      <dependency>
          <groupId>some.library</groupId>
          <artifactId>problematic-artifact</artifactId>
          <version>x.y.z</version>
          <exclusions>
              <exclusion>
                  <groupId>conflicting.group</groupId>
                  <artifactId>conflicting-artifact</artifactId>
              </exclusion>
          </exclusions>
      </dependency>
      
    • Gradle:
      implementation('some.library:problematic-artifact:x.y.z') {
          exclude group: 'conflicting.group', module: 'conflicting-artifact'
      }
      
  5. 使用 BOM (Bill of Materials): 对于大型项目或使用 Spring 等框架,可以使用 BOM 来统一管理一组相关依赖的版本。
    • Maven:
      <dependencyManagement>
          <dependencies>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-framework-bom</artifactId>
                  <version>6.0.11</version>
                  <type>pom</type>
                  <scope>import</scope>
              </dependency>
          </dependencies>
      </dependencyManagement>
      
  6. 离线模式: 构建工具支持离线模式(mvn -o, ./gradlew --offline),在已下载所有依赖后可以加速构建,但无法下载新依赖。

6. 最佳实践与性能优化

  1. 最佳实践:

    • 选择合适的构建工具: 团队统一。新项目可优先考虑 Gradle (Kotlin DSL)。
    • 版本控制 pom.xml/build.gradle: 将构建配置文件纳入版本控制(如 Git)。
    • 使用 Wrapper (Gradle):gradlew, gradlew.bat, gradle/wrapper/ 目录加入版本控制。
    • 清晰的依赖组织: 按功能或范围组织 dependencies 块。
    • 及时更新: 定期检查并更新 FreeMarker 和其他依赖到安全、稳定的版本(使用 mvn versions:display-dependency-updates, ./gradlew dependencyUpdates 插件)。
    • 最小化依赖: 只引入项目真正需要的依赖,避免臃肿。
    • 文档化: 在项目 README 中说明如何构建和运行。
  2. 性能优化 (构建过程):

    • Gradle Daemon: Gradle 默认启用 Daemon,一个后台进程,显著加快后续构建速度。确保它在运行。
    • 并行构建 (Gradle):gradle.properties 中设置 org.gradle.parallel=true,允许并行执行独立的任务。
    • 配置缓存 (Gradle 6.6+):gradle.properties 中设置 org.gradle.configuration-cache=true,缓存构建脚本的配置阶段,大幅缩短配置时间。
    • 增量编译: Maven 和 Gradle 都支持增量编译,只编译修改过的文件。
    • 构建扫描 (Gradle): 使用 --scan 参数生成构建报告,分析耗时任务。
    • Maven 优化: 使用 -T 选项进行并行构建(mvn -T 4 compile)。
    • 本地镜像/私服: 在企业环境中,搭建 Nexus 或 Artifactory 私服,作为中央仓库的镜像和缓存,加速依赖下载并提高稳定性。
    • 清理不必要的构建: 避免频繁执行 clean,除非必要(如解决缓存问题)。compile 通常比 clean compile 快。

通过遵循这些步骤和最佳实践,你可以轻松地将 FreeMarker 模板引擎集成到任何使用 Maven 或 Gradle 构建的 Java 项目中,享受自动化依赖管理带来的便利。