讲师中心 微信公众号
AI工具推荐 视频效率加速

Apache FreeMarker自定义指令与方法:在Java中处理模板参数值

千枫同学_4387

千枫同学_4387

发布时间:2025-11-22 14:58:11

|

232人浏览过

|

来源于php中文网

原创

apache freemarker自定义指令与方法:在java中处理模板参数值

FreeMarker允许开发者通过实现TemplateDirectiveModel或TemplateMethodModelEx接口,在Java代码中对模板参数进行自定义逻辑处理。本文将详细介绍如何利用这两种方式,在Java中获取并比较FreeMarker模板变量的值,并根据条件修改或返回新值,例如实现参数验证或格式化,以增强模板的动态性和灵活性。

FreeMarker模板引擎提供了强大的数据模型和表达式语言,但在某些场景下,我们需要在Java层面对模板变量进行更复杂的逻辑判断、数据转换或业务规则校验。例如,当一个模板变量需要根据特定条件进行格式化或替换时,纯粹的FreeMarker表达式可能无法满足需求,或者会导致模板逻辑过于复杂。本文将探讨如何通过自定义指令(TemplateDirectiveModel)和自定义方法(TemplateMethodModelEx)在Java代码中获取并处理FreeMarker模板参数,以实现诸如“检查 accNo 是否大于 0,否则设置为 0000”之类的业务逻辑。

1. 使用自定义指令 (TemplateDirectiveModel)

自定义指令类似于HTML标签,用于生成内容块或执行副作用。它通过实现TemplateDirectiveModel接口来实现,允许开发者在FreeMarker模板中定义自己的标签,并在Java代码中控制这些标签的行为。

1.1 获取参数方式

在TemplateDirectiveModel的execute方法中,可以通过两种主要方式获取模板参数:

立即学习“Java免费学习笔记(深入)”;

  • 作为指令参数传入:这是最常见且推荐的方式,参数直接通过指令的属性传递。例如,在模板中使用 ,则在Java代码中可以通过params Map获取到value参数。
  • 作为环境变量获取:如果指令本身不接受参数,但需要访问模板环境中已定义的变量(例如 ),则可以通过Environment对象的getVariable()方法获取。这种方式相对不推荐,因为它降低了指令的独立性和可读性。

1.2 Java实现示例 (AccNoDirective.java)

以下是一个实现TemplateDirectiveModel的Java类,它接收一个名为value的参数,检查其是否为大于0的数字,并根据条件输出原始值或"0000"。

import freemarker.core.Environment;
import freemarker.template.*;
import java.io.IOException;
import java.io.Writer;
import java.util.Map;

/**
 * 自定义FreeMarker指令,用于处理账户号码。
 * 如果账户号码大于0,则输出其值;否则输出"0000"。
 */
public class AccNoDirective implements TemplateDirectiveModel {

    @Override
    public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body)
            throws TemplateException, IOException {

        // 1. 尝试从指令参数中获取 'value'
        TemplateScalarModel valueModel = (TemplateScalarModel) params.get("value");
        String accNoStr = null;

        if (valueModel != null) {
            accNoStr = valueModel.getAsString();
        } else {
            // 2. 如果指令参数中没有 'value',则尝试从环境变量中获取 'accNo' (不推荐,但作为示例)
            TemplateModel accNoVar = env.getVariable("accNo");
            if (accNoVar instanceof TemplateScalarModel) {
                accNoStr = ((TemplateScalarModel) accNoVar).getAsString();
            } else if (accNoVar instanceof TemplateNumberModel) {
                accNoStr = ((TemplateNumberModel) accNoVar).getAsNumber().toString();
            }
        }

        String result = "0000"; // 默认值
        if (accNoStr != null && !accNoStr.trim().isEmpty()) {
            try {
                long accNo = Long.parseLong(accNoStr);
                if (accNo > 0) {
                    result = accNoStr;
                }
            } catch (NumberFormatException e) {
                // 处理非数字输入,可以抛出异常或输出错误信息
                env.getOut().write("Error: accNo parameter is not a valid number.");
                return;
            }
        }

        // 将结果写入输出流
        env.getOut().write(result);
    }
}

1.3 FreeMarker模板使用示例

为了在模板中使用上述指令,需要先在FreeMarker配置中注册它。假设已注册为accNoDirective。

<#-- 假设在Java代码中已注册 accNoDirective -->

<#-- 方式一:作为指令参数传入 (推荐) -->
<#assign accNo1 = 12345>
{ "Account_Number" : "<@accNoDirective value=accNo1 />" }
<#-- 输出: { "Account_Number" : "12345" } -->

<#assign accNo2 = 0>
{ "Account_Number" : "<@accNoDirective value=accNo2 />" }
<#-- 输出: { "Account_Number" : "0000" } -->

<#assign accNo3 = -5>
{ "Account_Number" : "<@accNoDirective value=accNo3 />" }
<#-- 输出: { "Account_Number" : "0000" } -->

<#assign accNo4 = "abc">
{ "Account_Number" : "<@accNoDirective value=accNo4 />" }
<#-- 输出: { "Account_Number" : "Error: accNo parameter is not a valid number." } -->

<#-- 方式二:从环境变量获取 (不推荐,仅作示例) -->
<#assign accNo = 67890>
{ "Account_Number" : "<@accNoDirective />" }
<#-- 输出: { "Account_Number" : "67890" } -->

<#assign accNo = 0>
{ "Account_Number" : "<@accNoDirective />" }
<#-- 输出: { "Account_Number" : "0000" } -->

注意事项: TemplateDirectiveModel主要用于生成内容或控制模板流。直接操作env.getOut().write()会将内容直接写入FreeMarker的输出流。

Wechat Article Html
Wechat Article Html

将 Markdown 文章转换为微信公众号兼容的纯 HTML 格式。当用户要求"转成微信格式"、"生成公众号文章 HTML"、"排版到微信草稿箱"、"微信粘贴格式"时触发。输出带内联样式的纯 HTML,微信编辑器可直接渲染,无需额外适配。支持标题、段落、列表、粗体、行内代码、表格、引用块、代码块、配图的完整转换。

下载

2. 使用自定义方法 (TemplateMethodModelEx)

自定义方法类似于函数,用于计算并返回一个值。它通过实现TemplateMethodModelEx接口来实现,是处理单个值转换或计算的更典型和推荐的方式。

2.1 获取参数方式

TemplateMethodModelEx的exec方法接收一个List类型的arguments参数,其中包含了FreeMarker模板中调用该方法时传入的所有参数。这些参数通常是TemplateModel的子类(如TemplateScalarModel、TemplateNumberModel等)。

2.2 Java实现示例 (AccNoMethod.java)

以下是一个实现TemplateMethodModelEx的Java类,它接收一个参数,检查其是否为大于0的数字,并返回原始值或"0000"。

import freemarker.template.TemplateMethodModelEx;
import freemarker.template.TemplateModelException;
import freemarker.template.TemplateScalarModel;
import freemarker.template.SimpleScalar;
import java.util.List;

/**
 * 自定义FreeMarker方法,用于处理账户号码。
 * 如果账户号码大于0,则返回其值;否则返回"0000"。
 */
public class AccNoMethod implements TemplateMethodModelEx {

    @Override
    public Object exec(List arguments) throws TemplateModelException {
        if (arguments.isEmpty()) {
            throw new TemplateModelException("AccNoMethod expects at least one argument.");
        }

        // 获取第一个参数
        Object arg0 = arguments.get(0);
        if (!(arg0 instanceof TemplateScalarModel)) {
            throw new TemplateModelException("The argument must be a scalar (string or number).");
        }
        TemplateScalarModel accNoModel = (TemplateScalarModel) arg0;
        String accNoStr = accNoModel.getAsString();

        String result = "0000"; // 默认值
        if (accNoStr != null && !accNoStr.trim().isEmpty()) {
            try {
                long accNo = Long.parseLong(accNoStr);
                if (accNo > 0) {
                    result = accNoStr;
                }
            } catch (NumberFormatException e) {
                // 抛出异常,让FreeMarker捕获并处理
                throw new TemplateModelException("Argument is not a valid number: " + accNoStr, e);
            }
        }
        // 必须返回一个TemplateModel对象
        return new SimpleScalar(result);
    }
}

2.3 FreeMarker模板使用示例

同样,为了在模板中使用此方法,需要在FreeMarker配置中注册它。假设已注册为accNoMethod。

<#-- 假设在Java代码中已注册 accNoMethod -->

<#assign accNo1 = 12345>
{ "Account_Number" : "${accNoMethod(accNo1)}" }
<#-- 输出: { "Account_Number" : "12345" } -->

<#assign accNo2 = 0>
{ "Account_Number" : "${accNoMethod(accNo2)}" }
<#-- 输出: { "Account_Number" : "0000" } -->

<#assign accNo3 = -5>
{ "Account_Number" : "${accNoMethod(accNo3)}" }
<#-- 输出: { "Account_Number" : "0000" } -->

<#assign accNo4 = "abc">
{ "Account_Number" : "${accNoMethod(accNo4)}" }
<#-- 这会抛出 TemplateModelException,FreeMarker会将其转换为模板错误 -->

注意事项: TemplateMethodModelEx必须返回一个TemplateModel对象(如SimpleScalar、SimpleNumber等)。

3. 注册自定义指令/方法

在Java应用程序中,需要将自定义指令或方法注册到FreeMarker的Configuration实例中,以便模板能够识别和使用它们。通常通过setSharedVariable方法完成。

import freemarker.template.Configuration;
import freemarker.template.Version;
import freemarker.template.TemplateExceptionHandler;

public class FreeMarkerConfigurator {

    public static Configuration createFreeMarkerConfiguration() {
        // 创建FreeMarker配置实例,并指定FreeMarker版本
        Configuration cfg = new Configuration(new Version("2.3.31"));

        // 设置模板加载器 (例如,从类路径加载模板)
        cfg.setClassForTemplateLoading(FreeMarkerConfigurator.class, "/templates");

        // 设置默认编码
        cfg.setDefaultEncoding("UTF-8");

        // 设置异常处理器 (开发环境通常设置为HTML_DEBUG_HANDLER,生产环境设置为RETHROW_HANDLER)
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);

        // 注册自定义指令
        cfg.setSharedVariable("accNoDirective", new AccNoDirective());

        // 注册自定义方法
        cfg.setSharedVariable("accNoMethod", new AccNoMethod());

        return cfg;
    }

    // 示例:如何使用配置渲染模板
    /*
    public static void main(String[] args) throws Exception {
        Configuration cfg = createFreeMarkerConfiguration();
        Template template = cfg.getTemplate("my_template.ftl");

        Map<String, Object> data = new HashMap<>();
        data.put("someVariable", "Hello");

        // 渲染模板到控制台
        template.process(data, new OutputStreamWriter(System.out));
    }
    */
}

4. 总结与最佳实践

  • 选择依据:
    • 当需要根据条件生成大段HTML、JSON或其他文本内容,或控制模板的执行流程时,使用TemplateDirectiveModel。它更适合处理有副作用或需要渲染复杂内容的场景。
    • 当需要对单个或少量参数进行计算、转换并返回一个值时,TemplateMethodModelEx是更简洁、更符合函数式编程思维的选择。对于本例中的“检查accNo并返回一个值”的需求,TemplateMethodModelEx是更优解,因为它专注于返回一个处理后的值,而不是直接操作输出流。
  • 类型处理: 在Java代码中,从TemplateModel转换为具体类型(如TemplateScalarModel、TemplateNumberModel)时,务必进行类型检查(instanceof)和异常处理(try-catch),以避免运行时错误。
  • 错误处理: 自定义方法和指令应妥善处理异常,例如参数类型不匹配、业务逻辑错误等。对于方法,可以通过抛出TemplateModelException来通知FreeMarker模板渲染失败;对于指令,可以直接向输出流写入错误信息,或者抛出异常。
  • 可读性与维护性: 优先使用指令或方法的参数来传递数据,而不是依赖环境变量。这能提高模板的可读性,使指令/方法的输入更加明确,也更易于测试和维护。
  • 性能考量: 自定义指令和方法会在每次模板渲染时执行其Java代码。确保其中的逻辑高效,避免不必要的计算或资源消耗。

通过合理地利用FreeMarker的自定义指令和方法

热门AI工具

更多
讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

UP简历
UP简历 Hot

一款AI办公效率工具,主要用于基于AI技术的免费在线简历制作工具,适合需要提升相关任务效率的用户。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

2195

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

3442

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

1156

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

3959

2025.09.10

json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

2195

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

3442

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

1156

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

3959

2025.09.10

Vite 项目如何打包和部署
Vite 项目如何打包和部署

Vite项目打包部署专题汇集生产构建、产物预览、输出目录、资源路径、Nginx与GitHub Pages部署及常见白屏和路由404问题的已审核文章,帮助读者完成上线流程。

0

2026.10.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
dev.java 官方:Learn Java
dev.java 官方:Learn Java

共0课时 | 0人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn