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

FreeMarker

Apache FreeMarker 是一个用于生成从HTML到电子邮件等各种文本输出的模板引擎。Spring框架内置了使用FreeMarker模板与Spring MVC集成的支持。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>

Alternatively, you can also declare the FreeMarkerConfigurer bean for full control over all properties, as the following example shows: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 配置

您可以直接将FreeMarker的“设置”和“共享变量”传递给由Spring管理的FreeMarker Configuration 对象,方法是设置FreeMarkerConfigurer bean的相应bean属性。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/> 元素。此元素主要用于让表单显示来自表单支持对象的值,并显示来自 web 或业务层中的 Validator 的验证失败结果。Spring 还支持在 FreeMarker 中使用相同的功能,并提供了额外的便利宏来生成表单输入元素。spring-doc.cadn.net.cn

绑定宏

一组标准的宏在spring-webmvc.jar文件中维护,用于FreeMarker,因此它们始终可用于配置适当的应用程序。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 宏假设由 ServletContext 参数 defaultHtmlEscapeweb.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 (根据代码参数从资源束中输出一个字符串)spring-doc.cadn.net.cn

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

messageText (根据代码参数从资源束中输出一个字符串,如果找不到则回退到默认参数的值)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 路径, 属性, 字段类型/>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 (一个列表框,允许用户选择0个或多个值)spring-doc.cadn.net.cn

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

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

<@spring.formRadioButtons 路径, 选项 分隔符, 属性/>spring-doc.cadn.net.cn

formCheckboxes (一组复选框,允许选择0个或多个值)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 分隔符, classOrStyle/>spring-doc.cadn.net.cn

In FreeMarker 模板中,formHiddenInputformPasswordInput 实际上并不是必需的,因为你可以使用正常的 formInput 宏,指定 hiddenpassword 作为 fieldType 参数的值。

上述任何宏的参数都有其一致的含义:spring-doc.cadn.net.cn

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

  • options: A Map of all the available values that can be selected from in the input field. The keys to the map represent the values that are POSTed back from the form and bound to the command object. Map objects stored against the keys are the labels displayed on the form to the user and may be different from the corresponding values posted back by the form. Usually, such a map is supplied as reference data by the controller. You can use any Map implementation, depending on required behavior. For strictly sorted maps, you can use a SortedMap (such as a TreeMap) with a suitable Comparator and, for arbitrary Maps that should return values in insertion order, use a LinkedHashMap or a LinkedMap from commons-collectionsspring-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 宏,包裹每个错误的元素使用的 CSS 类的名称。如果没有提供信息(或值为空),则错误将被包裹在 <b></b> 标签中。spring-doc.cadn.net.cn

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

输入字段

The formInput 宏接受 path 参数 (command.name) 和一个额外的 attributes 参数(在下面的例子中是空的)。该宏以及所有其他表单生成宏都会对路径参数执行隐式 Spring 绑定。绑定在新的绑定发生之前保持有效,因此 showErrors 宏不需要再次传递路径参数——它操作的是上次创建绑定的字段。spring-doc.cadn.net.cn

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

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

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

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

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

The formTextarea 宏的工作方式与 formInput 宏相同,并接受相同的参数列表。通常,第二个参数 (attributes) 用于传递样式信息或 rowscols 属性给 textareaspring-doc.cadn.net.cn

选择字段

您可以使用四个选择字段宏来生成常见的 UI 值选择输入,用于您的 HTML 表单: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 转义

默认情况下,前面描述的表单宏生成的HTML元素符合HTML 4.01标准,并使用在您的web.xml文件中定义的默认HTML转义值,如Spring绑定支持所使用的那样。要使元素符合XHTML标准或覆盖默认的HTML转义值,您可以在模板(或在模型中,它们对模板可见)中指定两个变量。在模板中指定它们的优点是可以在模板处理的后期将它们更改为不同的值,从而为表单中的不同字段提供不同的行为。spring-doc.cadn.net.cn

要将你的标签切换为XHTML兼容,请为名为xhtmlCompliant的模型或上下文变量指定值true,如下例所示: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 -->