很少使用且内置 maven

Page Contents

这些是通常不应该使用的内置函数,但是在特殊情况下(调试,高级宏),它们很有用。如果需要在常规页面模板中使用它们,则可以重新访问数据模型,因此不需要使用它们。

absolute_template_name

将模板名称转换为绝对名称,可以安全地传递给<#include name><#import name as ns>.get_optional_template(name),例如另一个模板,因为它不会被误解为相对于包含includeimport的模板目录等等。例如,如果您在模板"dir/here.ftl"中,则"target.ftl"将转换为"/dir/target.ftl"(请注意初始的/)。如果现在将此值传递给"other-dir/there.ftl"中的模板,并将其传递给include指令,则不会像"target.ftl"那样将其误解为"other-dir/target.ftl"

(可选)您可以指定将使用的基于根的名称(相对于模板根目录的名称,或者是绝对名称),而不是当前模板的名称,例如pathToConver?absolute_template_name(otherTemplateName)

应用程序示例(也使用.caller_template_name.get_optional_template):

<#--
  <@smileyInclude name /> behaves like <#include name>, but prints a "(:" before the
  template, or prints "):" instead if the template is missing.

  Note that just like with #include, if name is relative, it's resolved based on the
  directory of the caller template, not of the template that defines this macro. As
  .get_optional_template resolves relative names based on the current template, we
  had to convert the name to an absolute name based on the caller template before
  passing it to it.
-->
<#macro smileyInclude name>
  <#local t = .get_optional_template(
      name?absolute_template_name(.caller_template_name))>
  <#if t.exists>
    (:
    <@t.include />
  <#else>
    ):
  </#if>
</#macro>

api, has_api

Note:

这些内置功能自 FreeMarker 2.3.22 开始存在

如果值本身支持公开其 API,则value?api提供对value的 API(通常是 Java API)的访问,例如value?api.someJavaMethod()value?api.someBeanProperty。这意味着在需要调用对象的 Java 方法时很少使用,但是 FreeMarker 暴露给模板的值的设计简化视图将其隐藏了,并且也没有等效的内置函数。例如,当您将Map放入数据模型(并且使用默认的对象包装器)时,模板中的myMap.myMethod()基本上会转换为 Java 中的((Method) myMap.get("myMethod")).invoke(...),因此您无法调用myMethod。但是,如果您 RewritemyMap?api.myMethod(),则在 Java 中表示myMap.myMethod()。同样,myMap?api.myProperty在 Java 中转换为myMap.getMyProperty(),而不是myMap.get("myProperty")

*您应避免使用api,而应尽可能依赖 FTL 类型的功能和相关的内置函数.*例如,不要使用users?api.size(),而应使用users?size。当更改 FreeMarker 配置设置时,使用?api的变体更冗长,更慢,更容易中断,并且最重要的是,随着数据模型的技术细节更改,更容易中断。例如,如果将usersList更改为数组,则users?size将 continue 工作,而users?api.size()将断开。

避免调用“修改”对象的方法(尤其是Map -s 和Collection -s)或由于其他原因不是线程安全的方法。通常不希望模板修改暴露给它们的对象,只是为了显示它们。因此,应用程序可以将某些对象传递给多个(可能是并发的)模板处理。

内置的api并非处处可用,必须满足一些要求:

  • api_builtin_enabled配置设置必须设置为true。为了不降低现有应用程序的安全性,其默认值为false(至少从 2.3.22 开始)。

  • 价值本身必须支持它。我们正在谈论的是模板看到的值,它是通过object wrapping从原始对象(来自数据模型或 Java 方法返回值)创建的。因此,这取决于object_wrapper FreeMarker 配置设置以及被包装(原始)对象的类:

  • 当对象包装器的incompatibleImprovements设置为 2.3.22 或更高版本(看看如何在这里设置)的DefaultObjectWrapper时,由Map -s 和List -s 构成的 FTL 值支持?api。 (实际上,重要的是它的useAdaptersForContainer属性设置为true,但这是incompatibleImprovements的默认值.)如果DefaultObjectWrapperforceLegacyNonListCollections属性设置为false(默认值为false,则其他java.util.Collections(例如Set -s)仅支持?apitrue,以获得更好的现成的向后兼容性)。

    • 当用纯BeansWrapper包裹时,所有值都支持?api

    • 自定义TemplateModel -s 通过实现freemarker.template.TemplateModelWithAPISupport接口可以支持?api

如果在配置中不允许使用?api或不支持该值,则使用?api将中止模板处理,并出现错误。

可以像value?has_api一样检查一个值是否支持?api,它返回一个布尔值。请注意,?has_api的结果不受api_builtin_enabled设置的影响。

字节,双精度,浮点型,整数,长,短

返回一个SimpleNumber,该_包含与原始变量相同的值,但是使用java.lang.Type作为该值的内部表示。如果方法重载,或者TemplateModel解包器在自动选择合适的java.lang.*类型时遇到问题,这将很有用。请注意,自版本 2.3.9 起,解包器已得到实质性改进,因此,除了解决重载方法调用中的歧义之外,几乎不需要使用这些内置函数在数值类型之间进行转换。

内置long还可与日期,时间和日期时间值一起使用,以获取java.util.Date.getTime()返回的值。如果必须调用期望时间戳记为long的 Java 方法,这将很有用。

eval

该内置函数将字符串评估为 FTL * expression *。例如"1+2"?eval返回数字 3.(要渲染存储在字符串中的模板,请改用interpret built-in。)

评估的表达式将看到在调用eval时可见的相同变量(例如 locals)。也就是说,它的行为类似于在s?eval处具有* s的*值。除此之外,它不能使用内置循环变量来引用在s外部创建的循环变量。

关于影响解析(如语法)和评估的配置设置,规则与interpret built-in相同。

has_content

如果变量存在(不是 Java null)并且不是“空”,则为true,否则为false。 “空”的含义取决于具体情况。这遵循了直观的常识性想法。以下为空:长度为 0 的字符串,长度为 0 的标记输出值,没有子变量的序列或哈希,已传递最后一个元素的集合。如果值不是这些类型中的任何一个,则如果它是数字,日期或布尔值(例如0false不为空),则将其计为非空值,否则将计为空。请注意,当您的数据模型实现多个模板模型接口时,您可能会得到意外的结果。但是,如果有疑问,可以始终使用expr!?size > 0expr!?length > 0而不是expr?has_content

这种设置非常特殊,因为您可以像默认值运算符一样使用括号技巧。也就是说,您可以同时写入product.color?has_content(product.color)?has_content。当product丢失时,前一个不处理,最后一个不处理。

interpret

该内置函数将字符串解析为 FTL 模板,并返回执行该模板的用户定义指令,就像此时具有该内容的模板为include-d一样。例:

<#assign x=["a", "b", "c"]>
<#assign templateSource = r"<#list x as y>${y}</#list>">
<#-- Note: That r was needed so that the ${y} is not interpreted above -->
<#assign inlineTemplate = templateSource?interpret>
<@inlineTemplate />

The output:

abc

如您所见,inlineTemplate是用户定义的指令,执行后将运行其内容为templateSource值的模板。

interpret创建的模板的名称是调用interpret加上"->anonymous_interpreted"的模板的名称。例如,如果调用内置模板的模板为"foo/bar.ftl",那么生成的模板的名称为"foo/bar.ftl->anonymous_interpreted"。因此,解释的模板内部的相对路径是相对于此路径的(即,基本目录为"foo"),并且解释的模板内部的错误将指向此生成的模板名称。

有关更有用的错误消息,您可以在"->"之后覆盖模板名称部分。例如,假设mailTemplateSource来自mail_template数据库表,并且在发生错误的情况下,您希望错误日志包含失败模板的数据库 ID:

<#assign inlineTemplate = [mailTemplateSource, "mail_templates id=${mailTemplateId}"]?interpret>

如您所见,interpret可以应用于两个项目的序列,在这种情况下,第一个项目是要解释的 FTL 字符串,第二个项目是在"->"之后使用的模板名称。

影响解释模板的配置设置与周围模板相同,除了ftl directive中指定的解析器设置或通过标记语法或命名约定自动检测构建的解析器设置来自Configuration对象(或者自然地,来自TemplateConfiguration,如果有的话)。因此,解释模板的标签语法,命名约定,空白处理等与在周围模板内部*内部构建的标记无关。此规则的一个重要 exception 是output format和自动转义策略是从调用interpret的词法上下文中继承的。例如,在具有<#ftl output_format="XML">Headers 的模板中(或者,如果您在<#output_format "XML">...</#output_format>块中),在其中的interpret调用将生成 XML 输出格式的指令。

is_...

这些内置函数检查变量的类型,并根据类型返回truefalseis_...内置列表:

Built-in如果值为...,则传回true
is_stringstring
is_numbernumber
is_booleanboolean
is_date不要使用它!与is_date_like相同,请改用。以后可能会将含义更改为date_only
is_date_like类似日期的意思是日期,时间或日期时间,或者类似日期的未知精确类型(自 FreeMarker 2.3.21 起)
is_date_only日期(一天中没有时间)(自 FreeMarker 2.3.21 起)
is_time时间(无年月日部分)(自 FreeMarker 2.3.21 起)
is_datetime日期时间(包含年月日和一天中的时间)
is_unknown_date_like类似日期的地方,我们不知道它是日期,时间还是日期时间
is_methodmethod
is_transformtransform
is_macro宏或功能(是的,也用于功能;历史故障)
is_hash哈希(包括扩展哈希)
is_hash_ex扩展哈希(支持?keys?values)
is_sequence序列(历史怪癖:在incompatible_improvements 2.3.24 之前,它为 Java 方法返回true,因为它们实现了[index]运算符,但是在?size上失败。)
is_collection集合(包括扩展集合)
is_collection_ex扩展集合(支持?size)
is_enumerableSequences 或集合
is_indexable序列(历史古怪:当 Java 方法实现[index]运算符时,它将返回true.)
is_directive无论哪种指令(例如宏或TemplateDirectiveModelTemplateTransformModel等)或函数(历史故障)
is_nodenode
is_markup_output标记输出(一个值不会为auto-escaped)

markup_string

Note:

从 FreeMarker 2.3.24 开始,此内置功能可用。

以字符串形式返回存储在标记输出值内的标记。如果必须将值传递给String参数的 Java 方法,或者如果我们想直接在模板中操作标记,则这很有用。请注意,可以使用?no_esc将结果字符串转换回标记输出值。

namespace

此内置函数返回与宏或函数变量关联的名称空间(即名称空间的“门”哈希)。您只能将其与宏和函数一起使用。

new

这是为了创建某个TemplateModel实现的变量。

?的左侧,指定一个字符串,即TemplateModel实现的全限定类名。结果是一个方法变量,该方法变量调用构造函数,并返回新变量。

Example:

<#-- Creates an user-defined directive be calling the parameterless constructor of the class -->
<#assign word_wrapp = "com.acmee.freemarker.WordWrapperDirective"?new()>
<#-- Creates an user-defined directive be calling the constructor with one numerical argument -->
<#assign word_wrapp_narrow = "com.acmee.freemarker.WordWrapperDirective"?new(40)>

有关如何解开构造函数参数以及如何选择重载构造函数的更多信息,请阅读:程序员指南/其他/ Bean 包装器

由于模板作者可以创建任意 Java 对象,然后使用它们,只要它们实现了TemplateModel,则此内置可能会引起安全问题。模板创建者还可以为什至没有实现TemplateModel的类触发静态初始化。您可以(自 2.3.17 开始)使用Configuration.setNewBuiltinClassResolver(TemplateClassResolver)new_builtin_class_resolver设置来限制可使用此内置功能访问的类。有关更多信息,请参见 Java API 文档。如果您允许不太受信任的用户上载模板,那么您绝对应该研究此主题。

number_to_date,number_to_time,number_to_datetime

这些用于分别将数字(通常是 Java long)转换为日期,时间或日期时间。这与 Java 中的new java.util.Date(long)相同,也就是说,该数字被解释为自纪元以来经过的毫秒数。该数字可以是任何值,也可以是任何类型,只要其值适合long即可。如果该数字不是整数,则将使用上半部规则将其四舍五入为整数。

Example:

${1305575275540?number_to_datetime}
${1305575275540?number_to_date}
${1305575275540?number_to_time}

输出将是这样的(取决于当前的语言环境和时区):

May 16, 2011 3:47:55 PM
May 16, 2011
3:47:55 PM

sequence

此内置函数用于将可列出的值(您可以使用list directive进行迭代的值)转换为更强大的sequence值。序列支持xs[index]xs?size之类的操作。此外,即使原始值由java.util.Iterator支持(如果您尝试第二次将其列出也会产生错误),结果值也可以列出多次。如果您无法修复数据模型本身,则此内置函数通常用于解决数据模型问题。如果可以,请始终修复数据模型(将java.util.List或数组提供给模板,而不是受更严格限制的对象,例如 non_List java.util.Collectionjava.util.Iterator)。

如果该值已经是一个序列,则此内置函数将按原样返回该值。如果该值不是list directive可以列出的值,则模板处理将中止并出错。否则,它通常会获取所有值,并将它们存储到序列中。如果可以容纳大量物品,请小心,因为所有物品将同时保存在内存中。但是,在某些特殊情况下,避免了获取和/或存储所有元素。稍后再讨论optimizations

您只能使用sequence转换一次值。如果需要在多个位置生成结果序列,请始终将结果分配给变量,因为如果所转换的值仅可列出一次,则第二次对其进行转换将导致错误或空序列。此外,对于大型 collections 而言,转换成本较高,因此最好只进行一次转换。

示例:假设您发现users仅可列出一次(因为它是java.util.Iterator),但是您需要在模板中多次列出它,并且无法修复数据模型。然后,您可以这样做:

<#-- Collect all the users into a sequence: -->
<#assign usersSeq = users?sequence>

<#list usersSeq as user>...</#list>
Again:
<#list usersSeq as user>...</#list>

Optimizations

从版本 2.3.29 开始,如果内置sequence的结果直接是[index][range]运算符,?size?first或一系列操作的 Importing,则不会将这些元素收集到内存,只有严格必要的元素会被提取。例如anIterator?sequence[1]只会获取前 2 个项目(而不是构建包含所有元素的序列,然后从中获取第二个元素)。或者,如果您编写anIterator?sequence?size,它将跳过所有元素以对其进行计数,但不会将其存储在内存中。

优化将仅在相同的内置调用链中起作用,因此,例如在<#assign seq = anIterator?sequence>${seq[1]}中,由于anIterator?sequenceseq[1]被分离,因此?sequence步骤会将所有元素收集到内存中。另一方面,由于[range]?size都支持anIterator?sequence[10..]?size,因此优化将在anIterator?sequence[10..]?size中起作用,并且它们直接链接在一起。

with_args

Note:

自 2.3.30 起提供此内置功能

该内置函数的目标是“动态地”向指令(如宏),函数或方法的调用中添加参数。动态地表示参数是基于哈希值(例如{'a': 1, 'b': 2, 'c': 3}或 Java Map)或序列值(例如[1, 2, 3]或 Java List)添加的,其实际内容可能仅在调用发生时才知道。

例如,我们有这个宏m

<#macro m a b c>a=${a}, b=${b}, c=${c}</#macro>

通常,您这样称呼它:

<@m a=1 b=2 c=3 />

假设dynArgs是哈希{'a': 1, 'b': 2, 'c': 3},则下面的调用执行相同的操作:

<@m?with_args(dynArgs) />
a=1, b=1, c=1

下面的调用也这样做,但是结合了来自dynArgsAB的动态参数(假定为{'a': 1, 'b': 2})和直接指定的参数c

<@m?with_args(dynArgsAB) c=3 />
a=1, b=1, c=1

要理解为什么这样做,您需要认识到 FreeMarker 中的宏,自定义指令,函数和方法只是值,就像数字,字符串等一样。<#macro m ...>只是创建一个宏值(而不是数字,或者字符串等),然后将其分配给变量m。因此,m本身是一个有效的表达式,其结果为宏的值(但不会“调用”宏)。 <@m ... />计算表达式m(并且您也可以在其中使用任意复杂的表达式,例如m?with_args(...)),然后“调用”结果宏。 m?with_args(dynArgs)返回的宏与原始宏(存储在m中)非常相似,但其参数* default *为dynArgs中指定的值。因此m?with_args({'b': 22, 'c': 33})的结果类似于创建为<#macro unspefiedName a b=22 c=33>的修改后的宏。举一个例子:

<#assign mWithDefs = m?with_args({'b': 22, 'c': 33})>
<@myWithDefs a=1 c='overridden'/>
a=1, b=22, c=overridden

上面我们基于m的值创建了一个新的宏,将其存储在变量mWithDefs中,然后使用<@myWithDefs ... />对其进行了调用。

with_args也可以应用于函数(由<#function ...>组成)和 Java 方法(通常从数据模型中获取,例如myObject.myMethod)。但是由于函数和方法只能使用位置参数(例如f(1, 2, 3),而* not *作为f(a=1, b=2, c=3))调用,因此with_args的参数必须是序列而不是哈希。除此之外,与宏相同的技巧也起作用:

<#function f(a, b, c)><#return "a=${a}, b=${b}, c=${c}"></#function>
<#assign dynArgs=[1, 2, 3]>

${f(1, 2, 3)}
Same as:
${f?with_args(dynArgs)()}
or as:
${f?with_args([1, 2])(3)}
or as:
${f?with_args([1])(2, 3)}

<#assign fWithOneAsFirstArg = f?with_args([1])>
${fWithOneAsFirstArg(2, 3)} <#-- same as f(1, 2, 3) -->

请注意上面(...)的双重应用,例如f?with_args(dynArgs)()。这是因为f?with_args(dynArgs)只会返回一个新函数(只是一个值),而不会调用它。因此,如果要立即调用该新函数(而不是例如将其分配给变量),则需要第二个()

因为宏调用支持命名和位置参数,所以with_args参数也可以是宏的序列(尽管通常使用散列是更好的做法):

<#macro m a b c>a=${a}, b=${b}, c=${c}</#macro>

<#-- Called with named parameters: -->
<@m a=1 b=2 c=3 />
Same as:
<#-- Called with positional parameters: -->
<@m 1 2 3 />
Same as:
<@m?with_args([1, 2, 3]) />
Same as:
<#-- Sequence with_args with positional c parameter: -->
<@m?with_args([1, 2]) 3 />
Same as:
<#-- Sequence with_args with named c parameter: -->
<@m?with_args([1, 2]) c=3 />

总而言之,根据应用的值with_args的类型,with_args的参数类型可以是:

  • 功能或方法:Sequences。请注意 WRONG f?with_args(1, 2)是 WRONG,正确的格式是f?with_args([1, 2])

  • 宏:哈希或序列

  • 指令(用户定义):哈希

with_args的返回类型与它所应用的值的类型相同,例如,如果它应用于方法(如myObj.myMethod?with_args(dynArgs)),则它返回一个方法。

请注意,无法对内置指令(例如<#if ...><#list ...>等)应用with_args,因为它们不能作为值使用。

该内置函数通常与.args 特殊变量一起使用。例如:

<#macro m1 a b c>
  m1 does things with ${a}, ${b}, ${c}
</#macro>

<#macro m2 a b c>
  m2 does things with ${a}, ${b}, ${c}
  Delegate to m1:
  <@m1?with_args(.args) />
</#macro>

<@m2 a=1 b=2 c=3 />
m2 does things with 1, 2, 3
  Delegate to m1:
  m1 does things with 1, 2, 3

FreeMarker 语法允许在结束标记中的?with_args(...)之前使用名称,就像?with_args(...)不在那里一样:

<@myMacro?with_args({'a': 1})>...</@myMacro>

请注意,就参数的 Sequences 而言,来自with_args(...)的参数将添加到对返回的指令/函数/方法的调用中指定的参数之前。在某些情况下,最好在末尾添加它们,在这种情况下,请使用with_args_last built-in

with_args_last

Note:

自 2.3.30 起提供此内置功能

with_args相同,但如果最终最终参数列表中的参数 Sequences 可能有所不同(但其中的值没有不同)。仅当您按位置传递参数(通常是在调用函数或方法时)或存在所有参数时,这才有意义。

一个典型的带有位置参数的示例是当您要将动态参数添加到参数列表的末尾时:

<#function f a b c d>
  <#return "a=${a}, b=${b}, c=${c}, d=${d}">
</#function>

<#assign dynamicArgs=[3, 4]>

with_args:
${f?with_args(dynamicArgs)(1, 2)}

with_args_last:
${f?with_args_last(dynamicArgs)(1, 2)}
with_args:
a=3, b=4, c=1, d=2

with_args_last:
a=1, b=2, c=3, d=4

对于名称参数,虽然标识参数的主要方法是名称,但所有参数(以下others...)仍具有 Sequences:

<#macro m a b others...>
  a=${a}
  b=${b}
  others:
  <#list others as k, v>
    ${k} = ${v}
  </#list>
</#macro>

<#assign dynamicArgs={'e': 5, 'f': 6}>

with_args:
<@m?with_args(dynamicArgs) a=1 b=2 c=3 d=4 />

with_args_last:
<@m?with_args_last(dynamicArgs) a=1 b=2 c=3 d=4 />
with_args:
  a=1
  b=2
  others:
    e = 5
    f = 6
    c = 3
    d = 4

with_args_last:
  a=1
  b=2
  others:
    c = 3
    d = 4
    e = 5
    f = 6

如果指定的命名参数不是全部,那么它们将在macro标记中声明(如下面的ab所示),则with_argswith_args_last没什么不同,因为参数的 Sequences 是由宏定义指定的,而不是由宏定义的。宏调用:

<#macro m a=0 b=0>
  <#-- We use .args to demonstrate the ordering of a and b: -->
  <#list .args as k, v>
    ${k} = ${v}
  </#list>
</#macro>

<#assign dynamicArgs={'b': 1}>

with_args:
<@m?with_args(dynamicArgs) a=1 />

with_args_last:
<@m?with_args_last(dynamicArgs) a=1 />
with_args:
    a = 1
    b = 1

with_args_last:
    a = 1
    b = 1

如果宏或指令调用以及with_args_last参数都指定了具有相同名称的命名 catch-all 参数(例如下面的b),则这些参数的位置由宏/指令调用决定:

<#macro m others...>
  <#list others as k, v>
    ${k} = ${v}
  </#list>
</#macro>

<#assign dynamicArgs={'b': 0, 'd': 4}>

with_args:
<@m?with_args(dynamicArgs) a=1 b=2 c=3 />

with_args_last:
<@m?with_args_last(dynamicArgs) a=1 b=2 c=3 />
with_args:
    b = 2
    d = 4
    a = 1
    c = 3

with_args_last:
    a = 1
    b = 2
    c = 3
    d = 4