2.3.22
Page Contents
发布日期:2015-03-01
请注意,由于 2.3.22 被设计为与以前的 2.3.x 版本完全向后兼容,因此*仅在您明确要求 2.3.22“不兼容的改进”时才激活下面描述的某些改进和修补程序(始终很明显指出),因为它们极有可能破坏现有应用程序。对于主动维护的应用程序,最好允许它们。参见如何在此处设置“无法兼容的改进”。
FTL 方面的更改
- 新的内置插件:
api
和has_api
。如果值本身支持公开其 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")
。
如果可以,请尽可能依赖 FTL 类型和相关内置功能。只能使用?api
.
对于 FTL 的[]
运算符(如myMap[key]
)中缺少非String
Map
键的支持,使用?api
碰巧也提供了一种解决方法,因为现在您可以编写myMap?api.get(nonStringKey)
。
?api
默认未启用,并且并非适用于所有值。 在这里查看更多...
-
标识符(例如
someVariable
)现在可以在任何位置包含减号(-
),点(.
)和冒号(:
),但是这些字符必须以反斜杠(\
)进行转义,否则将被解释为操作员。例如,要读取名称为“ data-id”的变量,正确的表达式为data\-id
,因为data-id
将被解释为“数据减去 id”。这也适用于命名的宏参数,当您希望在包罗万象的参数中接受任意 HTML 属性(如<@box class="someCssClass" data\-id=product.id />
)时,该参数很有用。 (当您枚举宏内的所有参数名称时,获得的键字符串当然是"data-id"
而不是\
.) -
添加了
?lower_abc
和?upper_abc
。这会将1
,2
,3
等转换为字符串"a"
,"b"
,"c"
等(或"A"
,"B"
,"C"
等)。达到"z"
时,它会像"aa"
,"ab"
等那样 continue。这与您可以在电子表格应用程序(例如 Excel 或 Calc)的列标签中看到的逻辑相同。 More details... -
添加了
?keep_before_last
和?keep_after_last
。示例:"foo.bar.txt"?keep_before_last(".")
返回"foo.bar"
,"foo.bar.txt"?keep_after_last(".")
返回"txt"
。 (这些工作类似于?keep_before
和?keep_after
,但是它们寻找分隔符的第一个匹配项.) -
在合法标识符字符集中添加了许多缺少的 UNICODE 字母和数字,例如韩 Literals 母(错误已修复: [129])
-
错误消息质量改进:
-
调用自定义 JSP 标记时的一些改进;稍后在各自的部分中查看它们。
-
修正了错误:开始进行本地化查找或模板获取时,错误消息仍然引用了用于请求模板的名称,而不是实际的模板源名称(例如,在以
foo.ftl
的形式获取模板时,其名称为foo.ftl
而不是foo_en.ftl
,但是后面场景是从foo_en.ftl
加载的)。 -
现在,将更详细地说明“找不到模板”错误,并提供有关意外使用
\
而不是/
或退出TemplateLoader
的根目录的提示。 -
当无法识别设置名称时,
#setting
指令会给出更有用的错误消息,并列出允许的设置名称或更正建议。 -
遇到错误的特殊变量名称(
.name
)时,错误消息中将显示可用名称列表。 -
当包装的
Map
的Map.get
或Map.containsKey
抛出ClassCastException
或NullPointerException
时,错误将指向引起 FTL 表达式(有一些解释),而不是冒充为低级运行时错误。
-
Java 方面的更改
-
对象包装的改进:
-
DefaultObjectWrapper
,仅将其incompatible_improvements
设置为 2.3.22(看看这里如何...),或更准确地说,将其新的useAdaptersForContainers
设置设置为true
(当incompatible_improvements
设置为 2.3.22 时默认为true
):它不会复制Map
-s ,List
-s 和数组,然后将它们包装到TemplateModel
-s 中(通过模板访问所有值的接口),只需将它们包装到精简的TemplateModel
适配器中,这些适配器将到达所有操作的原始对象。包装的值将是新DefaultMapAdapter
,DefaultListAdapter
和DefaultArrayAdapter
类的实例,而不是旧的(复制)SimpleHash
和SimpleSequence
类。 (请注意,许多项目使用的是纯BeansWrapper
而不是DefaultObjectWrapper
,后者始终使用适配器方法,尽管实现方式有所不同.由于现在已修复DefaultObjectWrapper
的缺点,因此始终建议使用BeansWrapper
,因为BeansWrapper
会给人困惑的多类型值,并且速度要慢得多.)
尽管保持尽可能向后兼容性是此更改的一个重要因素,但这是一个非常深远的更改,因此您可能需要在这里查看结果和原因...(但是,默认情况下,此更改不处于活动状态,因此仅更新 FreeMarker 不会冒险现有应用程序的稳定性)
-
添加了
TemplateMethodModelEx BeansWrapper.wrap(Object object, Method method)
,用于包装方法而无需包装其父对象,也无需在调用时进行重载的方法选择。 -
错误修复\ [372]:
ClassCastException
当SortedMap
(通常是TreeMap
)被DefaultObjectWrapper
包裹后,会从中得到一个不存在的 1 个字符长的字符串。要解决此问题,如果包装的Map
是SortedMap
且被DefaultObjectWrapper
包装,则在String
键得到null
之后,它不会尝试退回到Character
键。 (此更改应向后兼容,因为当SortedMap
具有Character
键时,使用String
键的初始尝试会导致ClassCastException
,因此,此类SortedMap
-s 不能用作 FTL 哈希.) -
错误修复\ [368]:仅将
incompatible_improvements
设置为 2.3.22 或将其新的useAdaptersForContainers
设置设置为true
:键盘 Sequences 和“自定义”Map
类型的其他行为特性不再丢失。List
-s 也一样。 -
添加了新设置
forceLegacyNonListCollections
。仅当useAdaptersForContainers
为true
时才重要。然后,除非将其设置为true
,否则不是List
-s 的java.util.Collection
-s(例如Set
-s)将 continue 使用SimpleSequence
(即复制方法)而不是适配器方法。默认值是false
,至少到incompatible_improvements
2.4.0 为止,因为SimpleSequence
赋予了对这些非List
-s 的索引访问权限,就像mySet[2]
一样,这很奇怪,但是即使是偶然地,某些现有模板也可能会利用它。在forceLegacyNonListCollections
设置为false
的情况下,Set
-s 等都将无法进行索引访问(?first
和?last
也不起作用,但?size
仍然可以),因此您可能想要重新测试旧模板。另一方面,您可以获得适配器方法的优点。因此,在新项目中,强烈建议将forceLegacyNonListCollections
设置为false
。 (适配器方法由DefaultNonListCollectionAdapter
实现.) -
添加了新的实验性 FTL 类型的接口
freemarker.template.TemplateCollectionModelEx
,该接口将size()
,isEmpty()
和boolean contains(TemplateModel)
方法添加到TemplateCollectionModel
接口。之所以添加此功能,是因为在打包java.util.Collections
时,这些额外的功能区域仍然可用,但是 FTL 到目前为止无法使用它们。虽然确切的接口详细信息被标记为实验性的,但是当将DefaultObjectWrapper
的forceLegacyNonListCollections
属性设置为false
时,?size
已经使用了功能本身(请参见前面)。 -
添加了新的实验界面
freemarker.template.ObjectWrapperAndUnwrapper
。这扩展了ObjectWrapper
的展开功能。BeansWrapper
及其子类(如DefaultObjectWrapper
)已经存在很长时间了,但并未“分解”为其他ObjectWrapper
-s 可以实现的自己的已发布接口。这对于不需要BeansWrapper
(或其子类)的TemplateModel
实现非常有用,仅需要可解包功能的可用性。 -
添加了新的* experimental *接口来实现
?api
(请参见 FTL 部分):TemplateModelWithAPISupport
,ObjectAPIWrapper
,RichObjectWrapper
。请注意,尽管接口是实验性的,但?api
本身不是。 -
FreemarkerServlet
改进: -
FreemarkerServlet
现在支持自定义 JSP EL 函数(在具有function
XML 元素的 TLD-s 中定义)。此前它已经忽略了它们。可以像 Java 方法一样调用自定义 EL 函数,例如:<#assign u=JspTaglibs["/WEB-INF/utils.tld"]> ... ${u.truncate(title, 25)}
。-
错误修正:自定义标记参数的实际类型和预期类型之间存在类型不匹配时,错误消息无济于事。这是从 FTL 调用 JSP 标签库的用户经常遇到的问题(典型的“ java.lang.IllegalArgumentException:参数类型不匹配”,没有任何 FTL 上下文)。现在,这是一个适当的错误,包括解释,解决方案提示和 FTL 错误位置/引用。
-
已解决 RFE[113][114]:
FreemarkerServlet
现在可以发现META-INF/**/*.tld
-s 对类加载器可见,但不在WEB-INF/lib/*.jar
-s 中。为了使此功能有效,必须使用MetaInfTldSources
和/或ClasspathTlds
FreemarkerServlet
init-params 设置额外的 TLD 查找(有关这些说明,请参见FreemarkerServlet 的 Java API 文档)。例如,如果您使用嵌入式 Servlet 容器从 Eclipse 运行应用程序,因此标记库 jar-s 不在标准位置,而是像其他任何依赖项一样在 Classpath 中,那么您现在可以编写:
-
<init-param>
<param-name>MetaInfTldSources</param-name>
<param-value>classpath</param-value>
</init-param>
然后将在类加载器可见的所有META-INF
目录中搜索 TLD-s。
-
MetaInfTldSources
和ClasspathTlds
也可以分别附加到 Java 系统属性org.freemarker.jsp.metaInfTldSources
和org.freemarker.jsp.classpathTlds
的值或由其替换。因此,可以在 Eclipse 运行配置中调整这些参数,而无需修改web.xml
。 (有关详情,请参见FreemarkerServlet 的 Java API 文档。) -
FreemarkerServlet
现在可以识别org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern
servlet 上下文属性,并从中将条目添加到MetaInfTldSources
(上面介绍)。 -
添加了
protected FreemarkerServlet.createTaglibFactory()
以允许微调TaglibFactory
的设置。现在有一些设置器,例如setObjectWrapper
,setMetaInfTldSource
等。 -
添加了新的 servlet init-param
BufferSize
。如果响应状态仍然允许,则通过HTTPServletResponse.setBufferSize()
设置缓冲区大小,否则忽略它。 -
TemplatePath
servlet init-param 现在支持一种新的路径,看起来像classpath:com/example/myapp/templates
。这类似于旧的class://com/example/myapp/templates
,但是它使用初始化FreemarkerSerlvet
的线程的线程上下文类加载器,因此即使freemarker.jar
不在 Web 应用程序本地也可以使用。class://
具有使用FreemarkerSerlvet
本身(或其子类)的定义类加载器的问题。 -
如果
incompatible_improvements
设置为 2.3.22(或更高版本),则TemplatePath
servlet init-param 支持在[...]
内指定多个逗号分隔的路径,例如<param-value>[ WEB-INF/templates, classpath:com/example/myapp/templates ]</param-value>
。这在内部创建freemarker.cache.MultiTemplateLoader
。 -
添加了新的 servlet
init-param
,ExceptionOnMissingTemplate
。将此设置为true
可以将未找到模板错误的行为更改为类似于其他类型的模板异常(大多数设置为 HTTP 500“内部服务器错误”响应)。如果是false
(旧行为),则只会收到 HTTP 404“未找到”。尽管这也是 JSP 视图的工作方式,但事实证明这是一个问题,因为如果 MVC 视图提供 404,则某些框架也会向访问者提供 404.但是要转到转发到 MVC 视图的地步,访问者必须访问有效的 URL,只有该页面错过了它的视图,因此它在服务器端损坏了,因此应该是 500. -
添加了新的可覆盖方法:
FreemarkerServlet.createDefaultObjectWrapper()
。这可以用于通常被createObjectWrapper()
覆盖的对象,但不会不情愿地禁用相关 init-params(如object_wrapper
)的处理。 -
改进的(或固定的)错误日志记录:现在,日志将始终进入 FreeMarker 自己的日志,而不仅仅是 servlet 容器日志。此外,根据 Servlet 容器的不同,有时还会丢失早期的模板未找到和模板解析错误详细信息日志。
-
错误已修复,仅在
incompatible_improvements
设置为 2.3.22(或更高版本)时有效:放入 JSP * page *范围(通过#global
或通过 JSPPageContext
API)并在以后使用 JSPPageContext
API 读回时的某种值(通常在自定义 JSP 标记中),可能会以 FreeMarkerTemplateModel
对象而不是标准 Java 类型的对象的形式返回。其他 Servlet 范围不受影响。某些人期望这个错误的可能性很小。受影响的值属于下面列出的 FTL 类型,要触发该错误,必须直接在模板中创建它们(例如,作为 FTLLiterals 或使用?date
/time
/datetime
),或者您必须使用DefaultObjectWrapper
或SimpleObjectWrapper
(或它们的子类): -
FTL 日期/时间/日期时间值可能会以
freemarker.template.SimpleDate
-s 的形式返回,现在却以java.util.Date
-s 的形式返回。-
FTL 序列值可能会以
SimpleSequence
-s 的形式返回,现在按预期以java.util.List
-s 的形式返回。这是假定object_wrapper
配置设置是BeansWrapper
的子类(例如DefaultObjectWrapper
),但是在使用 FreeMarker 的 JSP 扩展的应用程序中实际上总是这样(否则它仍然可以工作,但这取决于ObjectWrapper
的质量和功能)实施)。 -
FTL 哈希值可能以
SimpleHash
-es 的形式返回,现在又按预期以java.util.Map
-s 的形式返回(再次,假设对象包装器是BeansWrapper
的子类)。 -
FTL 集合值可能以
SimpleCollection
-s 的形式返回,现在又按预期以java.util.Collection
-s 的形式返回(再次,假设对象包装器是BeansWrapper
的子类)。 -
错误修复:现在可以在
WEB-INF/
及其所有子目录中递归搜索*.tld
文件。之前,它们仅在WEB-INF/
和WEB-INF/lib/
下直接搜索。 -
修正了错误:删除了
name
和tag-class
元素内 TLD-s 中的前导和尾随空格。 -
纠正了不良行为:如果多个 TLD-sMap 到相同的标记库 URI,则
WEB-INF/**/*.tld
-s 的优先级高于来自 jar-s 或 Classpath 目录的META-INF/**/*.tld
-s 的优先级。较早的情况是相反的,只是META-INF/lib/*.tld
-s 仍然可以随机优先。尽管 JSP 规范(2.2)明确指出未定义 Sequences,并且不应依赖该 Sequences,但逻辑上是,如果有人将 TLD 直接放在WEB-INF
下,他的意思是将其用于该特定 Web 应用程序,而不是 TLD 来自通常由多个 Web 应用程序共享的依赖项 jar。 -
修正了错误:覆盖
FreemarkerServlet.createConfiguration
中设置的默认值不会再被FreemarkerServlet
的出厂默认值覆盖。仅这些设置有问题:template_exception_handler
,log_template_exceptions
,object_wrapper
,template_loader
。 -
修正了错误:如果在同一 servlet 上下文中有多个
FreemarkerServlet
-s 具有不同的配置设置,则可能导致故障。 (通常,您只有一个,就像只有一个处理*.jsp
的 servlet 一样.) -
从 FreeMarker 工件和 XML 实体解析器中删除了所有
xsd
文件(web-app
和taglib
模式),因为在 XML 解析期间未使用它们。 -
总体上提高了实施质量(可维护性,错误消息,性能错误修复,测试覆盖率)和更好的 API 文档。
-
-
日志记录功能的改进:
-
就像之前一样,当自动选择 Logger 库时(默认行为),FreeMarker 选择 Log4j(如果可用)。但是现在,如果结果是
log4j-over-slf4j
,则 FreeMarker 将直接使用 SLF4J。 (这解决了记录的位置指向 FreeMarker 的日志适配器类而不是实际呼叫位置的问题.)-
FreeMarker 现在可以识别
org.freemarker.loggerLibrary
系统属性,该属性指定要使用的 Logger,例如java ... -Dorg.freemarker.loggerLibrary=SLF4J
。此选项弃用Logger.selectLoggerLibrary(int)
,因为它固有地不可靠(因为您通常无法很好地控制类的初始化 Sequences)。系统属性的优先级高于Logger.selectLoggerLibrary
。 -
总体上提高了实施质量(出现故障时会打印更多信息,等等)。
-
新的配置设置:
log_template_exceptions
(Configuration.setLogTemplateExceptions(boolean)
)。这指定 FreeMarker 是否记录由模板处理引发的TemplateException
-s。缺省值为true
以实现向后兼容性,但这会导致在正确编写的应用程序中两次记录该异常,因为调用者还会记录由公共 FreeMarker API 抛出的TemplateException
(即使仅作为更高级别异常的原因 exception) )。因此,在现代应用中,应将其设置为false
。 (请注意,此设置对#attempt
/#recover
捕获的异常的记录没有影响;始终记录这些异常.)
-
-
Environment
和自定义指令相关的改进: -
添加了
Environment.getCurrentDirectiveCallPlace()
,当从自定义指令(即TemplateDirectiveModel.execute()
)中调用时,该对象返回DirectiveCallPlace
对象。DirectiveCallPlace
对象使您可以将任意对象与模板内的指令调用相关联,该模板可用于调用位置限制的缓存(如缩小非动态嵌套内容)。有关更多信息,请参见 Java API 文档中的DirectiveCallPlace
。- 添加了
Environment.getMainTemplate()
。不再使用含糊不清(并且经常损坏: [145])Environment.getTemplate()
。
- 添加了
-
Template loading:
-
添加了新的
Configuration
设置template_lookup_strategy
(Configuration.setTemplateLookupStrategy(TemplateLookupStrategy)
)。这允许自定义在请求模板时将尝试的TemplateLoader
级别名称。例如,通过此方法,您可以定义自定义的本地化查找序列,而不是默认值(看起来像是foo_de_LU_MAC.ftl, foo_de_LU.ftl, foo_de.ftl,
foo.ftl
)。-
添加了新的
Configuration.getTemplate(...)
参数Object customLookupCondition
。可以通过自定义TemplateLookupStrategy
来使用此参数,以从请求的名称中推断出实际的模板名称(类似于默认查找策略基于语言环境进行的查找)。例如,在一个多域网站上,可能要定义一些专门用于域的模板,然后将域名用作自定义查找条件。然后,当请求foo.ftl
时,自定义TemplateLookupStrategy
可以先查找@somedomain.com/foo.ftl
,然后查找@default/foo.ftl
。有关更多详细信息,请参见相关Configuration.getTemplate(...)
重载的 JavaDoc。请注意有关customLookupCondition
的hashCode
和equals
的要求。 -
添加了新的
Configuration
设置template_name_format
(Configuration.setTemplateNameFormat(TemplateNameFormat)
)。这允许指定 FreeMarker 使用的命名规则。目前,不允许自定义实现,您只能在TemplateNameFormat.DEFAULT_2_3_0
(默认)和DEFAULT_2_4_0
(推荐,至少对于新项目)中选择。DEFAULT_2_4_0
具有多个优点,但并不完全向后兼容(尽管大多数应用程序都不会受到影响)。对于典型的错误,例如使用反斜杠代替斜杠,或者退出根目录,它给出MalformedTemplateNameFormatException
而不是TempalteNotFoundException
。它允许方案名称仅用:
而不是://
(也受支持)而不是classpath:foo/bar.ftl
终止。它修复了许多遗留的故障(错误),这些故障主要与.
或*
之类的特殊步骤后对..
的解释有关。请参见TemplateNameFormat.DEFAULT_2_4_0 的 Java API 文档中的差异的完整列表。 -
现在,可以直接指定
ClassLoader
来创建ClassTemplateLoader
,而不必指定基本Class
。也就是说,现在有ClassTemplateLoader(ClassLoader, String)
构造函数,还有Configuration.setClassLoaderForTemplateLoading(ClassLoader, String)
方法。 -
添加了新的 exception
TemplateNotFoundException
,现在在获取模板时将使用它代替TemplateNotFoundException
。随着TemplateNotFoundException
的扩展,此更改向后兼容。主要目的是解决常见的误解,即模板路径是真实文件路径。但是,新异常还有一个好处,就是它可以提供有关错误的其他 FreeMarker 特定信息,就像现在它具有getTemplateName()
和getCustomLookupCondition()
方法一样。 -
除了
getName()
之外,Template
-s 现在具有getSourceName()
方法。只要没有本地化的查找或获取(名称中为*
)或未积极参与其他查找策略,这两个返回的结果相同。但是,当getSourceName()
给出实际从TemplateLoader
加载模板的名称时,而getName()
返回(并且一直返回)请求模板的名称(以规范形式)。getName()
用于所有内容(如相对包含解析),但错误消息中的位置信息除外,该信息现在使用getSourceName()
。而且,TemplateException
现在具有getSourceName()
方法。 -
Configuration.getTemplate(...)
重载现在接受__locale
和encoding
参数,在这种情况下,它们将使用与忽略参数的重载相同的默认值。 -
调试器 SPI 实现者,注意:
DebugBreak
指令现在会将模板的sourceName
发送到suspendEnvironmentSpi
回调,而不是其name
。您还应该在registerTemplateSpi
中使用sourceName
,而不是name
。
-
-
Configuration:
-
为多种设置添加了
Configuration.unsetXxx
和isXxxExplicitlySet
方法。取消设置会使其表现得好像从未调用过setXxx
,因此该设置将使用适合当前incompatible_improvements
值的默认值,并将在以后更改incompatible_improvements
时进行调整。- 从
java.util.Properties
(或通常使用String
-String
名称/值对)配置 FreeMarker 时:
- 从
-
default
设置值现在可以通过template_exception_handler
,template_storage
,template_loader
(以及新的template_lookup_strategy
和template_name_format
)设置识别,并导致Configuration.unsetXxx()
被调用。-
错误修正:将
object_wrapper
设置为default
(而不是未指定)时,它已忽略incompatible_improvements
并始终使用ObjectWrapper.DEFAULT_WRAPPER
。此修补程序仅在incompatible_improvements
恰好是 2.3.21 时才有意义,因为那是将默认对象包装器从ObjectWrapper.DEFAULT_WRAPPER
更改为new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_21).build()
的结果时,该结果有点不同的单例,因为它具有只读配置设置和错误修复重载方法选择规则。要使用ObjectWrapper.DEFAULT_WRAPPER
而不考虑incompatible_improvements
设置的值,请使用新的default_2_3_0
值。 -
修正了错误:更改
localized_lookup
设置的值现在会清空模板缓存,因此不会重复使用旧的查询结果。 (这仅在您在已经运行的服务下更改此设置时才有意义,这是非常不可能的.)
-
-
Miscellaneous:
-
错误修复\ [145],仅在
incompatible_improvements
设置为 2.3.22(或更高版本)时有效:#include
和#nested
不再将Environment
的父Template
(请参见Configurable.getParent()
)更改为包含的Template
或#nested
“返回”到。因此,Environment
的父代将始终是主要的Template
。 (主要的Template
是调用其process
或createProcessingEnvironment
方法以启动输出生成的Template
.)请注意,这仅在您直接在Template
对象上设置设置时才有意义(不要与通过#setting
在模板中设置设置相混淆)。修改Environment
,因此不受此修复程序的影响),几乎没有人这样做。还要注意,宏调用从未将Environment
父级更改为包含宏定义的Template
,因此现在没有任何更改。-
已修复错误\ [419]:当 FreeMarker 没有读取 Java 系统属性的权限时(例如在未签名的 applet 中使用时),它不会再失败。它只是记录一些警告。
-
由于频繁滥用,
HTML_DEBUG
和DEBUG
TemplateExceptionHandler
输出现在包含警告,例如“ HTML_DEBUG 模式;在 Producing 使用 RETHROW!”。 -
模板规范形式输出中的一些修复和改进,并因此而在 FTL 堆栈跟踪指令显示中得到了改善。
-
将某些历史上已公开但内部 API 标记为已弃用,因此免责声明在 IDE 中更为明显。
-
Notes
当其不兼容的改进设置为 2.3.22 时,在DefaultObjectWrapper
中为容器类型引入适配器方法的后果和原因:
-
使用新方法(适配器方法),永远不会丢失
Map
-s 的键 Sequences。复制方法只能保留HashMap
个子类(例如LinkedHashMap
)和SortedMap
-s(例如TreeMap
),而不能保留更奇特的Map
-s,例如 Guava 的ImmutableMap
。而且,现在保留了原始Map
的其他任何行为 Feature(例如,不区分大小写的键查找)。 -
当将包装的值从模板传递回 Java 方法时,将保留
Map
/List
的确切类型和标识。使用传统方法,Java 方法已收到特定于 FreeMarker 特定类型的Map
或List
(充当TemplateModel
的适配器)。 -
性能 Feature 通常会有所改善,但这取决于应用程序。如果模板从数据模型读取 same(!)条目大约一次或两次(或根本不读取),这很典型,那么它们的适配器方法会提供更好的结果,否则旧版复制方法会更快(因为它可以重用之前阅读的包装条目),尽管对于大多数应用程序而言,这种减慢速度肯定不是问题。新适配器方法的性能更可预测,因为它没有初始的“尖峰”来设置容器副本(特别是对于庞大的集合而言是痛苦的),而性能却与数据模型读取的数量成线性比例(而不是与收集条目的数量)。
-
如果将
Map
/List
/array 换行后对其进行了更改,则更改现在将在数据模型中可见。使用复制方法时,包装的值是原始Map
/List
/array 的浅表快照。尽管不太可能有人故意使用此功能,但这是切换到适配器时的风险因素。 -
从理论上讲,某些代码(主要是
TemplateDirectiveModel
实现)可能会错误地认为包装的Map
-s 是SimpleHash
-es,包装的List
-s 是SimpleSequence
-s 等,而不是仅仅是TemplateHashModel
-s 和TemplateSequenceModel
-s。这样的代码总是错误的,但是现在它确实会中断,因此这是一个风险因素。 -
由于现在已包装原始对象的确切类型用于重载方法选择,因此选择可以有所不同(并且类似于纯
BeansWrapper
)。很难找到重要的案例。阵列最有可能发生更改,因为使用复制方法时,将它们解包到List
-s 而不是原始数组。因为重载的方法机制可以在数组和列表之间(双向)转换,所以通常这不是问题。但是,当重载方法在数组的参数位置上具有各种类型时,它不会在不同的数组类型之间进行转换,因此这是一个风险因素。 -
SimpleHash
和SimpleSequence
尚未被弃用。它们仍然用于在 FTL 中创建的哈希和序列,并且建议将其用于专门为从模板使用而构建的值,而不是包装已存在的Map
或List
或数组。 -
现在,在多线程只读访问下,暴露给多线程模板的
List
-s 和Map
-s 在其正确操作方面承受更大的压力。这是因为适配器在从多个线程访问它们之前,不会将其内容复制到众所周知的List
和Map
实现(HashMap
,ArrayList
等)中。因此,这主要与自定义List
和Map
实现有关,它们不如标准 Java 类成熟。请注意,这始终与纯BeansWrapper
相同,它被许多项目/框架(如 Struts)使用了很长时间,因此这并不是一个未知的领域。 -
当包装的
List
是AbstractSequentialList
(例如LinkedList
)时,结果适配器将实现TemplateCollectionModel
以实现更有效的枚举(#list
-ing),当然除了TemplateSequenceModel
之外。TemplateCollectionModel
允许 FTL 遍历列表,而无需按索引访问元素。使用旧版复制方法TemplateCollectionModel
并没有实现,因为在那里不需要进行有效的枚举。 -
迭代器(当您将它们直接放入数据模型中时)包装在
DefaultIteratorAdapter
而不是SimpleCollection
中。这有两个结果: -
现在,将包装的
Iterator
从模板传递到 Java 方法后,可以正确地将其包装到原始 Java 对象中。- 包装的
Iterator
-s(不要与Iterable
混淆)不再是线程安全的,毕竟要保留一些同步,将相同的Iterator
暴露给多个并行模板执行没有多大意义。这不应该是迁移问题,因为甚至在更早的时候,这些模板执行中只有一个可以成功(未复制Iterator
-s 的“内容”,因此第一次访问它的人成为了独占所有者)。所做的更改仅是早先保证了其他线程将失败(这是线程安全的),而现在没有此类保证。
- 包装的