FreeMarker自定义TemplateDirectiveModel

本文介绍了如何在FreeMarker模板中使用自定义宏来减少重复性工作,通过扩展FreeMarker接口实现在后端定义宏,从而在渲染模板时动态获取数据,避免在多个Controller接口中添加相同的数据。

在采用FreeMarker做前台视图模板的情况下,我们可以通过<#include>标签和自定义宏来解决很多重复性工作。

一个简单的FreeMarker宏:

<#macro sayHello name="">
    hello ${name}
</#macro>
然后通过如下的形式调用:

<@sayHello name="shannon" />
不过这种在模板页中定义的宏能力有限。【1】假设,我们很多页面都要输出一个热门排行框,而排行数据需要从controller层动态获取,我们可以用这种宏来完成所有的展示工作,但前提是相应的controller和接口中层需要预先将这些排行数据放到model中去,因此对于后端来说这也是一个重复性的工作。那么有没有一种方式可以让后端也脱离这种重复工作呢?答案是肯定的,这也是写这篇博客的目的。

在一个偶然的机会发现jeecms项目中用到了这种方式,于是借鉴了一番。

FreeMarker不仅可以在前端的模板页中定义宏,还可以通过扩展其接口在后端实现宏,这有什么好处呢?这种方式就好比让你的模板页具备了从前端再次回到后端的能力。这样我们就能很好的解决【1】处的假设,我们无需在各个controller的各个接口中去重复的向model中添加所需的排行数据,而是当FreeMarker渲染模板页时遇到相应的宏它可以回到后端去调用相应的方法取到所需的数据。例子如下:

import freemarker.core.Environment;
import freemarker.template.ObjectWrapper;
import freemarker.template.TemplateDirectiveModel;

/**
 * FreeMarker自定义宏
 * 获取App下载排
<think>好的,我现在要处理用户的问题,关于如何在FreeMarker自定义函数的示例。首先,我需要回顾之前的对话历史,确保理解用户的需求和之前的回答。用户之前询问了Layui添加类名的问题,但现在转向了FreeMarker自定义函数,这属于模板引擎的扩展应用。 用户提到了站内引用,引用1和引用2都涉及到FreeMarker自定义方法,特别是引用1详细说明了步骤,而引用2提到了分页标签。用户现在明确要求一个自定义函数的示例,所以需要结合这些引用内容来构建答案。 首先,我需要确认用户的需求:他们希望了解如何在FreeMarker中创建自定义函数,可能需要一个具体的示例。根据引用1,用户可能已经知道Freemarker支持自定义方法,但需要步骤和代码示例。用户的问题可能包括如何定义、注册以及使用这些函数。 接下来,我需要回忆Freemarker自定义函数的基本步骤。通常,这涉及到实现TemplateMethodModelEx或TemplateDirectiveModel接口,然后注册到配置中。对于简单的函数,可能使用TemplateMethodModelEx更合适,因为它处理返回值较为直接。 然后,我需要考虑用户可能的实际应用场景。例如,用户可能需要字符串处理、数据格式化或分页功能,如引用2提到的分页标签。但用户这次明确询问的是函数,所以可能更倾向于方法而非标签。因此,示例应该选择一个通用的功能,比如字符串处理或数学计算,以便用户容易理解。 另外,需要确保回答结构清晰,分步骤说明,并包含代码示例。同时,根据用户之前的提问习惯,他们可能希望看到详细的代码注释和注意事项,比如配置文件的修改和模板中的调用方式。 还要注意用户提到的“serverId在后台用不到”,这可能意味着他们希望在前端模板中处理某些逻辑,而不依赖后端VO字段。因此,自定义函数可以帮助他们在模板中直接处理数据,减少后端传递的数据量。 最后,生成相关问题,帮助用户深入理解,比如参数处理、异常情况或复杂函数的使用。同时,确保引用标注正确,引用1和2的信息需要适当融入回答中,但用户可能更关注实际步骤和示例,所以重点放在如何实现自定义函数,而不是分页标签,除非用户示例需要扩展。 总结:回答应包括步骤概述、具体代码示例(Java类和Freemarker配置)、模板调用方式以及注意事项,最后提出相关问题。需要确保语言简洁,代码正确,并参考之前的引用内容,但以用户当前需求为主。</think>根据 FreeMarker 官方文档和实际项目经验,自定义函数的实现可分为三个核心步骤。以下通过字符串格式化函数的完整示例演示具体实现: --- ### 一、核心实现步骤 #### 1. 创建 Java 类实现 `TemplateMethodModelEx` ```java // 字符串格式化函数示例:${format("Hello {0}", "World")} public class FormatMethod implements TemplateMethodModelEx { @Override public Object exec(List arguments) throws TemplateModelException { // 参数校验 if (arguments.size() < 1) throw new TemplateModelException("至少需要1个参数"); // 转换参数类型 String pattern = ((SimpleScalar)arguments.get(0)).getAsString(); Object[] params = arguments.stream().skip(1) .map(arg -> ((SimpleScalar)arg).getAsString()) .toArray(); // 核心逻辑实现 return MessageFormat.format(pattern, params); } } ``` #### 2. 注册函数到 FreeMarker 配置 ```xml <!-- springfreemarker.xml 配置示例 --> <bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> <property name="freemarkerVariables"> <map> <entry key="format" value-ref="formatMethod"/> </map> </property> </bean> <bean id="formatMethod" class="com.example.FormatMethod"/> ``` #### 3. 模板调用函数 ```ftl <#-- 调用示例 --> ${format("当前时间:{0,date,yyyy-MM} | 状态:{1}", .now?datetime, "已激活")} ``` --- ### 二、进阶用法示例 #### 带对象处理的日期计算函数 ```java // 日期加减函数:${dateAdd("2023-05-20", 7, "days")} public class DateAddMethod implements TemplateMethodModelEx { @Override public Object exec(List args) throws TemplateModelException { LocalDate date = LocalDate.parse(((SimpleScalar)args.get(0)).getAsString()); int amount = ((SimpleNumber)args.get(1)).getAsNumber().intValue(); String unit = ((SimpleScalar)args.get(2)).getAsString(); TemporalUnit chronoUnit = ChronoUnit.valueOf(unit.toUpperCase()); return date.plus(amount, chronoUnit).toString(); } } ``` --- ### 三、最佳实践建议 1. **类型安全处理** 建议使用 `TemplateModelUtils` 进行参数类型转换,避免直接强制类型转换导致的 `ClassCastException` 2. **异常处理** 通过 `TemplateModelException` 返回友好错误信息: ```java if (arguments.size() < 3) { throw new TemplateModelException("需要三个参数:dateStr, addValue, timeUnit"); } ``` 3. **性能优化** 对于高频调用的函数,推荐: - 使用对象池复用实例 - 避免在 `exec()` 方法内创建临时对象 --- ### 四、调试技巧 在模板开发阶段可通过 `?is_method` 检测函数可用性: ```ftl <#if format?is_method> 函数已正确注册 <#else> !!! 函数注册失败 !!! </#if> ``` ---
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值