FreeMarker
Apache FreeMarker 是一个模板引擎,用于生成各种文本输出,从 HTML 到电子邮件等。Spring 框架内置了与 FreeMarker 模板一起使用的 Spring MVC 的集成。
视图配置
以下示例展示了如何将 FreeMarker 配置为视图技术:
- Java
- Kotlin
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.freeMarker();
}
// Configure FreeMarker...
@Bean
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath("/WEB-INF/freemarker");
configurer.setDefaultCharset(StandardCharsets.UTF_8);
return configurer;
}
}
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
override fun configureViewResolvers(registry: ViewResolverRegistry) {
registry.freeMarker()
}
// Configure FreeMarker...
@Bean
fun freeMarkerConfigurer() = FreeMarkerConfigurer().apply {
setTemplateLoaderPath("/WEB-INF/freemarker")
setDefaultCharset(StandardCharsets.UTF_8)
}
}
以下示例展示了如何在 XML 中配置相同的内容:
<mvc:annotation-driven/>
<mvc:view-resolvers>
<mvc:freemarker/>
</mvc:view-resolvers>
<!-- Configure FreeMarker... -->
<mvc:freemarker-configurer>
<mvc:template-loader-path location="/WEB-INF/freemarker"/>
</mvc:freemarker-configurer>
或者,您也可以声明 FreeMarkerConfigurer
bean,以便完全控制所有属性,如以下示例所示:
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
<property name="defaultEncoding" value="UTF-8"/>
</bean>
您的模板需要存储在前面示例中所示的 FreeMarkerConfigurer
指定的目录中。根据前面的配置,如果您的控制器返回视图名称 welcome
,解析器将查找 /WEB-INF/freemarker/welcome.ftl
模板。
FreeMarker 配置
您可以通过在 FreeMarkerConfigurer
bean 上设置适当的 bean 属性,将 FreeMarker 的 'Settings' 和 'SharedVariables' 直接传递给 FreeMarker Configuration
对象(由 Spring 管理)。freemarkerSettings
属性需要一个 java.util.Properties
对象,而 freemarkerVariables
属性需要一个 java.util.Map
。以下示例展示了如何使用 FreeMarkerConfigurer
:
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
<property name="freemarkerVariables">
<map>
<entry key="xml_escape" value-ref="fmXmlEscape"/>
</map>
</property>
</bean>
<bean id="fmXmlEscape" class="freemarker.template.utility.XmlEscape"/>
请参阅 FreeMarker 文档,以获取有关设置和变量如何应用于 Configuration
对象的详细信息。
表单处理
Spring 提供了一个用于 JSP 的标签库,其中包含一个 <spring:bind/>
元素。此元素主要允许表单显示来自表单支持对象的值,并显示来自 Validator
的验证失败结果,适用于 web 或业务层。Spring 还在 FreeMarker 中支持相同的功能,并提供了额外的便利宏来生成表单输入元素。
绑定宏
一组标准的宏保存在 spring-webmvc.jar
文件中供 FreeMarker 使用,因此它们始终可用于适当配置的应用程序。
在 Spring 模板库中定义的一些宏被视为内部(私有),但在宏定义中并不存在这样的作用域,这使得所有宏对调用代码和用户模板都是可见的。以下部分仅集中于您需要直接从模板中调用的宏。如果您希望直接查看宏代码,文件名为 spring.ftl
,位于 org.springframework.web.servlet.view.freemarker
包中。
简单绑定
在基于 FreeMarker 模板的 HTML 表单中,这些表单作为 Spring MVC 控制器的表单视图,你可以使用类似于下一个示例的代码来绑定字段值,并以类似于 JSP 的方式为每个输入字段显示错误消息。以下示例展示了一个 personForm
视图:
<!-- FreeMarker macros have to be imported into a namespace.
We strongly recommend sticking to 'spring'. -->
<#import "/spring.ftl" as spring/>
<html>
...
<form action="" method="POST">
Name:
<@spring.bind "personForm.name"/>
<input type="text"
name="${spring.status.expression}"
value="${spring.status.value?html}"/><br />
<#list spring.status.errorMessages as error> <b>${error}</b> <br /> </#list>
<br />
...
<input type="submit" value="submit"/>
</form>
...
</html>
<@spring.bind>
需要一个 'path' 参数,该参数由你的命令对象的名称组成(默认为 'command',除非你在控制器配置中进行了更改),后面跟一个点和你希望绑定到命令对象上的字段名称。你还可以使用嵌套字段,例如 command.address.street
。bind
宏假定使用 web.xml
中 ServletContext
参数 defaultHtmlEscape
指定的默认 HTML 转义行为。
一种名为 <@spring.bindEscaped>
的宏的替代形式接受第二个参数,该参数明确指定是否应在状态错误消息或值中使用 HTML 转义。您可以根据需要将其设置为 true
或 false
。其他表单处理宏简化了 HTML 转义的使用,您应尽可能使用这些宏。它们将在下一节中解释。
输入宏
额外的便利宏用于 FreeMarker 简化了绑定和表单生成(包括验证错误显示)。生成表单输入字段时,使用这些宏并不是必需的,您可以将它们与简单的 HTML 或直接调用我们之前强调的 Spring 绑定宏混合使用。
以下可用宏的表格显示了 FreeMarker 模板 (FTL) 定义及其所需的参数列表:
表 1. 宏定义表
宏 | FTL 定义 |
---|---|
message (根据代码参数从资源包中输出字符串) | <@spring.message code/> |
messageText (根据代码参数从资源包中输出字符串,如果没有找到则使用默认参数的值) | <@spring.messageText code, text/> |
url (用应用程序的上下文根前缀相对 URL) | <@spring.url relativeUrl/> |
formInput (用于收集用户输入的标准输入字段) | <@spring.formInput path, attributes, fieldType/> |
formHiddenInput (用于提交非用户输入的隐藏输入字段) | <@spring.formHiddenInput path, attributes/> |
formPasswordInput (用于收集密码的标准输入字段。请注意,此类型的字段永远不会填充值。) | <@spring.formPasswordInput path, attributes/> |
formTextarea (用于收集长文本输入的大文本字段) | <@spring.formTextarea path, attributes/> |
formSingleSelect (下拉框选项,允许选择一个必填值) | <@spring.formSingleSelect path, options, attributes/> |
formMultiSelect (选项列表框,允许用户选择 0 个或多个值) | <@spring.formMultiSelect path, options, attributes/> |
formRadioButtons (一组单选按钮,允许从可用选项中进行单一选择) | <@spring.formRadioButtons path, options separator, attributes/> |
formCheckboxes (一组复选框,允许选择 0 个或多个值) | <@spring.formCheckboxes path, options, separator, attributes/> |
formCheckbox (单个复选框) | <@spring.formCheckbox path, attributes/> |
showErrors (简化绑定字段的验证错误显示) | <@spring.showErrors separator, classOrStyle/> |
在 FreeMarker 模板中,formHiddenInput
和 formPasswordInput
实际上不是必需的,因为您可以使用普通的 formInput
宏,指定 hidden
或 password
作为 fieldType
参数的值。
上述任何宏的参数具有一致的含义:
-
path
: 绑定的字段名称(例如,"command.name") -
options
: 一个Map
,包含可以在输入字段中选择的所有可用值。映射的键表示从表单 POST 回来的值,并绑定到命令对象上。存储在键上的 Map 对象是显示在表单上的标签,可能与表单 POST 回来的相应值不同。通常,这样的映射由控制器作为参考数据提供。您可以根据所需的行为使用任何Map
实现。对于严格排序的映射,您可以使用带有适当Comparator
的SortedMap
(例如TreeMap
),而对于应该按插入顺序返回值的任意映射,可以使用LinkedHashMap
或来自commons-collections
的LinkedMap
。 -
separator
: 当多个选项作为离散元素(单选按钮或复选框)可用时,用于分隔列表中每个选项的字符序列(例如<br>
)。 -
attributes
: 要包含在 HTML 标签本身内的额外任意标签或文本的字符串。该字符串由宏逐字回显。例如,在textarea
字段中,您可以提供属性(例如 'rows="5" cols="60"),或者您可以传递样式信息,例如 'style="border:1px solid silver"'。 -
classOrStyle
: 对于showErrors
宏,包裹每个错误的span
元素使用的 CSS 类名称。如果没有提供信息(或值为空),则错误将被包裹在<b></b>
标签中。
以下部分概述了宏的示例。
输入字段
formInput
宏接受 path
参数(command.name
)和一个额外的 attributes
参数(在接下来的示例中为空)。该宏以及所有其他表单生成宏会对 path
参数执行隐式的 Spring 绑定。绑定在新的绑定发生之前保持有效,因此 showErrors
宏不需要再次传递 path
参数 — 它作用于最后创建绑定的字段。
showErrors
宏接受一个分隔符参数(用于分隔给定字段上的多个错误的字符),并且还接受第二个参数 — 这次是类名或样式属性。请注意,FreeMarker 可以为属性参数指定默认值。以下示例展示了如何使用 formInput
和 showErrors
宏:
<@spring.formInput "command.name"/>
<@spring.showErrors "<br>"/>
下面的示例展示了表单片段的输出,生成名称字段,并在表单提交时该字段没有值的情况下显示验证错误。验证通过 Spring 的验证框架进行。
生成的 HTML 类似于以下示例:
Name:
<input type="text" name="name" value="">
<br>
<b>required</b>
<br>
<br>
formTextarea
宏的工作方式与 formInput
宏相同,并接受相同的参数列表。通常,第二个参数 (attributes
) 用于传递样式信息或 textarea
的 rows
和 cols
属性。
选择字段
您可以使用四个选择字段宏在您的 HTML 表单中生成常见的 UI 值选择输入:
-
formSingleSelect
-
formMultiSelect
-
formRadioButtons
-
formCheckboxes
每个宏都接受一个 Map
类型的选项,其中包含表单字段的值以及与该值对应的标签。值和标签可以相同。
下面的示例是 FTL 中的单选按钮。表单支持对象为此字段指定了默认值 'London',因此不需要验证。当表单被渲染时,供选择的城市完整列表作为参考数据在模型中以 'cityMap' 的名称提供。以下列表展示了这个示例:
...
Town:
<@spring.formRadioButtons "command.address.town", cityMap, ""/><br><br>
前面的列表渲染了一行单选按钮,每个按钮对应 cityMap
中的一个值,并使用了分隔符 ""
。没有提供额外的属性(宏的最后一个参数缺失)。cityMap
在映射中对每个键值对使用相同的 String
。映射的键是表单实际作为 POST
请求参数提交的内容。映射的值是用户看到的标签。在前面的示例中,给定一个包含三个知名城市的列表和一个在表单支持对象中的默认值,生成的 HTML 类似于以下内容:
Town:
<input type="radio" name="address.town" value="London">London</input>
<input type="radio" name="address.town" value="Paris" checked="checked">Paris</input>
<input type="radio" name="address.town" value="New York">New York</input>
如果您的应用程序预计通过内部代码(例如)处理城市,您可以创建带有合适键的代码映射,如下例所示:
- Java
- Kotlin
protected Map<String, ?> referenceData(HttpServletRequest request) throws Exception {
Map<String, String> cityMap = new LinkedHashMap<>();
cityMap.put("LDN", "London");
cityMap.put("PRS", "Paris");
cityMap.put("NYC", "New York");
Map<String, Object> model = new HashMap<>();
model.put("cityMap", cityMap);
return model;
}
protected fun referenceData(request: HttpServletRequest): Map<String, *> {
val cityMap = linkedMapOf(
"LDN" to "London",
"PRS" to "Paris",
"NYC" to "New York"
)
return hashMapOf("cityMap" to cityMap)
}
代码现在生成的输出中,单选按钮的值是相关的代码,但用户仍然看到更友好的城市名称,如下所示:
Town:
<input type="radio" name="address.town" value="LDN">London</input>
<input type="radio" name="address.town" value="PRS" checked="checked">Paris</input>
<input type="radio" name="address.town" value="NYC">New York</input>
HTML 转义
默认使用前面描述的表单宏会生成符合 HTML 4.01 标准的 HTML 元素,并使用在你的 web.xml
文件中定义的 HTML 转义的默认值,这些值由 Spring 的绑定支持使用。为了使元素符合 XHTML 标准或覆盖默认的 HTML 转义值,你可以在模板中(或在模型中,使其对模板可见)指定两个变量。在模板中指定它们的好处是,可以在模板处理的后期将其更改为不同的值,以便为表单中的不同字段提供不同的行为。
要将您的标签切换为 XHTML 兼容性,请为名为 xhtmlCompliant
的模型或上下文变量指定一个值为 true
,如下例所示:
<#-- for FreeMarker -->
<#assign xhtmlCompliant = true>
处理完这个指令后,由 Spring 宏生成的任何元素现在都符合 XHTML 标准。
以类似的方式,您可以为每个字段指定 HTML 转义,如以下示例所示:
<#-- until this point, default HTML escaping is used -->
<#assign htmlEscape = true>
<#-- next field will use HTML escaping -->
<@spring.formInput "command.name"/>
<#assign htmlEscape = false in spring>
<#-- all future fields will be bound with HTML escaping off -->