对于最新稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

FreeMarker

Apache FreeMarker 是一个模板引擎,可用于生成各种类型的文本输出,包括 HTML、电子邮件等。Spring Framework 内置了对在 Spring MVC 中使用 FreeMarker 模板的支持。spring-doc.cadn.net.cn

视图配置

以下示例展示了如何将 FreeMarker 配置为视图技术:spring-doc.cadn.net.cn

@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");
		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")
	}
}

以下示例展示了如何在 XML 中进行相同的配置:spring-doc.cadn.net.cn

<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 来完全控制所有属性,如下例所示:spring-doc.cadn.net.cn

<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
	<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
</bean>

您的模板需要存储在前例中所示的 FreeMarkerConfigurer 所指定的目录中。根据上述配置,如果您的控制器返回视图名称 welcome,解析器将查找 /WEB-INF/freemarker/welcome.ftl 模板。spring-doc.cadn.net.cn

FreeMarker 配置

你可以通过在 Configuration bean 上设置相应的 bean 属性,将 FreeMarker 的 'Settings' 和 'SharedVariables' 直接传递给 FreeMarker 的 FreeMarkerConfigurer 对象(该对象由 Spring 管理)。其中,freemarkerSettings 属性需要一个 java.util.Properties 对象,而 freemarkerVariables 属性则需要一个 java.util.Map。以下示例展示了如何使用 FreeMarkerConfigurerspring-doc.cadn.net.cn

<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-doc.cadn.net.cn

表单处理

Spring 提供了一个用于 JSP 的标签库,其中包含(但不限于)一个 <spring:bind/> 元素。该元素主要用于在表单中显示表单支持对象(form-backing objects)的值,并展示 Web 层或业务层中 Validator 验证失败的结果。Spring 还为 FreeMarker 提供了相同功能的支持,并额外提供了便捷的宏(macros),用于直接生成表单输入元素。spring-doc.cadn.net.cn

绑定宏

FreeMarker 的一组标准宏定义维护在 spring-webmvc.jar 文件中,因此对于经过适当配置的应用程序而言,这些宏始终可用。spring-doc.cadn.net.cn

Spring 模板库中定义的一些宏被视为内部(私有)宏,但在宏定义中并不存在此类作用域限制,因此所有宏对调用代码和用户模板都是可见的。以下各节仅聚焦于您需要在模板中直接调用的宏。如果您希望直接查看宏的源代码,该文件名为 spring.ftl,位于 org.springframework.web.servlet.view.freemarker 包中。spring-doc.cadn.net.cn

简单绑定

在基于 FreeMarker 模板的 HTML 表单中(这些表单作为 Spring MVC 控制器的表单视图),您可以使用类似于以下示例的代码来绑定字段值,并以与 JSP 等效方式类似的方式显示每个输入字段的错误消息。以下示例展示了一个 personForm 视图:spring-doc.cadn.net.cn

<!-- 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.streetbind 宏会采用 ServletContextdefaultHtmlEscape 参数 web.xml 所指定的默认 HTML 转义行为。spring-doc.cadn.net.cn

另一种形式的宏称为 <@spring.bindEscaped>,它接受第二个参数, 用于显式指定在状态错误消息或值中是否应使用 HTML 转义。您可以根据需要将其设置为 truefalse。 额外的表单处理宏简化了 HTML 转义的使用,您应尽可能使用这些宏。 它们将在下一节中进行说明。spring-doc.cadn.net.cn

输入宏

FreeMarker 提供了额外的便捷宏,用于简化数据绑定和表单生成(包括验证错误信息的显示)。使用这些宏来生成表单输入字段并非必需,您可以将它们与普通 HTML 或之前介绍过的直接调用 Spring 绑定宏的方式混合使用。spring-doc.cadn.net.cn

下表列出了可用的宏,展示了每个宏对应的 FreeMarker 模板(FTL)定义及其参数列表:spring-doc.cadn.net.cn

表1. 宏定义表
FTL 定义

message(根据 code 参数从资源包中输出一个字符串)spring-doc.cadn.net.cn

<@spring.message code/>spring-doc.cadn.net.cn

messageText(根据 code 参数从资源包中输出一个字符串,如果找不到则回退到 default 参数的值)spring-doc.cadn.net.cn

<@spring.messageText code, text/>spring-doc.cadn.net.cn

url(在相对 URL 前添加应用程序的上下文根路径)spring-doc.cadn.net.cn

<@spring.url relativeUrl/>spring-doc.cadn.net.cn

formInput(用于收集用户输入的标准输入字段)spring-doc.cadn.net.cn

<@spring.formInput path, attributes, fieldType/>spring-doc.cadn.net.cn

formHiddenInput(用于提交非用户输入的隐藏输入字段)spring-doc.cadn.net.cn

<@spring.formHiddenInput path, attributes/>spring-doc.cadn.net.cn

formPasswordInput(用于收集密码的标准输入字段。请注意,此类字段永远不会填充任何值。)spring-doc.cadn.net.cn

<@spring.formPasswordInput path, attributes/>spring-doc.cadn.net.cn

formTextarea(用于收集长段自由格式文本输入的大型文本框)spring-doc.cadn.net.cn

<@spring.formTextarea path, attributes/>spring-doc.cadn.net.cn

formSingleSelect(下拉选项框,用于选择一个必填的值)spring-doc.cadn.net.cn

<@spring.formSingleSelect path, options, attributes/>spring-doc.cadn.net.cn

formMultiSelect(一个选项列表框,允许用户选择零个或多个值)spring-doc.cadn.net.cn

<@spring.formMultiSelect path, options, attributes/>spring-doc.cadn.net.cn

formRadioButtons(一组单选按钮,允许从可用选项中进行单一选择)spring-doc.cadn.net.cn

<@spring.formRadioButtons path, options separator, attributes/>spring-doc.cadn.net.cn

formCheckboxes(一组复选框,允许选择零个或多个值)spring-doc.cadn.net.cn

<@spring.formCheckboxes path, options, separator, attributes/>spring-doc.cadn.net.cn

formCheckbox(单个复选框)spring-doc.cadn.net.cn

<@spring.formCheckbox path, attributes/>spring-doc.cadn.net.cn

showErrors(简化显示绑定字段的验证错误)spring-doc.cadn.net.cn

<@spring.showErrors separator, classOrStyle/>spring-doc.cadn.net.cn

在 FreeMarker 模板中,formHiddenInputformPasswordInput 实际上并不是必需的,因为你可以使用普通的 formInput 宏,并将 hidden 参数的值指定为 passwordfieldType

上述任意宏的参数具有统一的含义:spring-doc.cadn.net.cn

  • path:要绑定到的字段名称(例如,“command.name”)spring-doc.cadn.net.cn

  • options:一个Map,包含输入字段中所有可供选择的值。该映射的键代表从表单提交并绑定到命令对象的值。存储在键对应的映射对象是显示在表单上供用户查看的标签,可能与表单提交回来的相应值不同。通常,此类映射由控制器作为参考数据提供。根据所需行为,您可以使用任何Map实现。对于严格排序的映射,您可以使用带有合适ComparatorSortedMap(例如TreeMap);对于需要按插入顺序返回值的任意映射,请使用来自commons-collectionsLinkedHashMapLinkedMapspring-doc.cadn.net.cn

  • separator:当多个选项以独立元素(如单选按钮或复选框)形式提供时,用于分隔列表中每个选项的字符序列(例如 <br>)。spring-doc.cadn.net.cn

  • attributes:一个额外的任意标签或文本字符串,将被包含在 HTML 标签本身之内。该字符串会被宏原样输出。例如,在 textarea 字段中,您可以提供属性(如 'rows="5" cols="60"'),也可以传入样式信息,例如 'style="border:1px solid silver"'。spring-doc.cadn.net.cn

  • classOrStyle:对于 showErrors 宏,指定用于包裹每个错误信息的 span 元素所使用的 CSS 类名。如果没有提供信息(或值为空),则错误信息将被包裹在 <b></b> 标签中。spring-doc.cadn.net.cn

以下各节概述了宏的示例。spring-doc.cadn.net.cn

输入字段

formInput 宏接受 path 参数(command.name)以及一个额外的 attributes 参数(在接下来的示例中为空)。该宏与所有其他表单生成宏一样,会对 showErrors 参数执行隐式的 Spring 绑定。此绑定会一直有效,直到发生新的绑定为止,因此 5 宏无需再次传递 6 参数——它会直接作用于最近一次创建绑定的字段。spring-doc.cadn.net.cn

showErrors 宏接受一个分隔符参数(用于分隔同一字段上的多个错误信息的字符),还接受第二个参数——这次是一个类名或样式属性。请注意,FreeMarker 可以为 attributes 参数指定默认值。以下示例展示了如何使用 formInputshowErrors 宏:spring-doc.cadn.net.cn

<@spring.formInput "command.name"/>
<@spring.showErrors "<br>"/>

下一个示例展示了表单片段的输出,生成了名称字段,并在提交表单时该字段为空的情况下显示验证错误。验证通过 Spring 的验证框架进行。spring-doc.cadn.net.cn

生成的 HTML 类似于以下示例:spring-doc.cadn.net.cn

Name:
<input type="text" name="name" value="">
<br>
	<b>required</b>
<br>
<br>

formTextarea 宏的使用方式与 formInput 宏相同,并接受相同的参数列表。通常,第二个参数(attributes)用于传递样式信息,或为 rows 元素指定 colstextarea 属性。spring-doc.cadn.net.cn

选择字段

您可以使用四个选择字段宏,在 HTML 表单中生成常用 UI 值选择输入控件:spring-doc.cadn.net.cn

这四个宏中的每一个都接受一个包含表单字段值及其对应标签的 Map 选项。值和标签可以相同。spring-doc.cadn.net.cn

下一个示例是 FTL 中的单选按钮。表单绑定对象为此字段指定了默认值“London”,因此无需进行验证。在渲染表单时,可供选择的完整城市列表作为引用数据以名称“cityMap”提供在模型中。以下代码清单展示了该示例:spring-doc.cadn.net.cn

...
Town:
<@spring.formRadioButtons "command.address.town", cityMap, ""/><br><br>

上述代码片段会渲染一行单选按钮,cityMap 中的每个值对应一个单选按钮,并使用 "" 作为分隔符。未提供额外的属性(宏的最后一个参数被省略了)。在 cityMap 中,映射的每个键值对都使用相同的 String。该映射的键才是表单实际作为 POST 请求参数提交的内容,而映射的值则是用户看到的标签。在前面的例子中,假设有三个知名城市的列表,并且表单支持对象中设定了默认值,生成的 HTML 类似如下:spring-doc.cadn.net.cn

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>

如果你的应用程序需要通过内部代码(例如)来处理城市,你可以创建一个以合适键值为索引的代码映射,如下例所示:spring-doc.cadn.net.cn

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)
}

现在的代码生成的输出中,单选按钮的值是相应的代码,但用户看到的仍然是更友好的城市名称,如下所示:spring-doc.cadn.net.cn

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 转义

如前所述,默认使用表单宏(form macros)会生成符合 HTML 4.01 标准的 HTML 元素,并采用 web.xml 文件中定义的默认 HTML 转义值(该值由 Spring 的绑定支持所使用)。若要使生成的元素符合 XHTML 标准,或覆盖默认的 HTML 转义值,您可以在模板中(或在模型中,只要模板可以访问到这些变量)指定两个变量。在模板中指定这些变量的优势在于,在模板处理过程中,您可以随时将它们更改为不同的值,从而为表单中的不同字段提供不同的行为。spring-doc.cadn.net.cn

要使您的标签符合 XHTML 规范,请为名为 true 的模型或上下文变量指定值 xhtmlCompliant,如下例所示:spring-doc.cadn.net.cn

<#-- for FreeMarker -->
<#assign xhtmlCompliant = true>

处理此指令后,Spring 宏生成的所有元素现在都符合 XHTML 标准。spring-doc.cadn.net.cn

同样地,您可以按字段指定 HTML 转义,如下例所示:spring-doc.cadn.net.cn

<#-- 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 -->