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

JSP 和 JSTL

Spring 框架内置了对在 Spring MVC 中使用 JSP 和 JSTL 的集成支持。spring-doc.cadn.net.cn

视图解析器

在使用 JSP 进行开发时,通常会声明一个 InternalResourceViewResolver bean。spring-doc.cadn.net.cn

InternalResourceViewResolver 可用于转发到任何 Servlet 资源,尤其适用于 JSP 页面。作为最佳实践,我们强烈建议将 JSP 文件放置在 'WEB-INF' 目录下的某个子目录中,以防止客户端直接访问。spring-doc.cadn.net.cn

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
	<property name="prefix" value="/WEB-INF/jsp/"/>
	<property name="suffix" value=".jsp"/>
</bean>

JSP 与 JSTL 对比

使用 JSP 标准标签库(JSTL)时,必须使用一个特殊的视图类 JstlView,因为 JSTL 需要进行一些准备工作,才能使国际化(I18N)等功能正常工作。spring-doc.cadn.net.cn

Spring 的 JSP 标签库

Spring 提供了将请求参数绑定到命令对象的功能,如前几章所述。为了便于在 JSP 页面开发中结合使用这些数据绑定特性,Spring 提供了一些标签,使开发更加简便。所有 Spring 标签都具备 HTML 转义功能,可启用或禁用字符的转义。spring-doc.cadn.net.cn

spring.tld 标签库描述符(TLD)包含在 spring-webmvc.jar 中。 有关各个标签的完整参考,请浏览 API 参考文档 或查看标签库描述。spring-doc.cadn.net.cn

Spring 的表单标签库

从 2.0 版本开始,Spring 提供了一整套支持数据绑定的标签,用于在使用 JSP 和 Spring Web MVC 时处理表单元素。每个标签都支持其对应 HTML 标签的所有属性,使得这些标签易于理解和直观使用。这些标签生成的 HTML 符合 HTML 4.01/XHTML 1.0 规范。spring-doc.cadn.net.cn

与其他表单/输入标签库不同,Spring 的表单标签库与 Spring Web MVC 紧密集成,使这些标签能够访问控制器所处理的命令对象和引用数据。正如我们在以下示例中所展示的那样,表单标签使得 JSP 的开发、阅读和维护变得更加简单。spring-doc.cadn.net.cn

我们逐一介绍表单标签,并查看每个标签的使用示例。对于某些需要进一步说明的标签,我们还包含了生成的 HTML 代码片段。spring-doc.cadn.net.cn

配置

表单标签库内置于 spring-webmvc.jar 中。该库的描述文件名为 spring-form.tldspring-doc.cadn.net.cn

要使用此库中的标签,请在您的 JSP 页面顶部添加以下指令:spring-doc.cadn.net.cn

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

其中 form 是您希望用于此库中标签的标签名称前缀。spring-doc.cadn.net.cn

表单标签

此标签渲染一个 HTML 'form' 元素,并向内部标签暴露一个绑定路径以进行绑定。它将命令对象放入 PageContext 中,以便内部标签可以访问该命令对象。本标签库中的所有其他标签都是 form 标签的嵌套标签。spring-doc.cadn.net.cn

假设我们有一个名为User的领域对象。它是一个JavaBean,具有诸如firstNamelastName等属性。我们可以将其用作表单控制器的表单支持对象(form-backing object),该控制器返回form.jsp。以下示例展示了form.jsp可能的样子:spring-doc.cadn.net.cn

<form:form>
	<table>
		<tr>
			<td>First Name:</td>
			<td><form:input path="firstName"/></td>
		</tr>
		<tr>
			<td>Last Name:</td>
			<td><form:input path="lastName"/></td>
		</tr>
		<tr>
			<td colspan="2">
				<input type="submit" value="Save Changes"/>
			</td>
		</tr>
	</table>
</form:form>

firstNamelastName 的值是从页面控制器放入 PageContext 中的命令对象中获取的。请继续阅读,以查看有关如何在 form 标签中使用内部标签的更复杂示例。spring-doc.cadn.net.cn

以下清单展示了生成的 HTML,其外观类似于标准表单:spring-doc.cadn.net.cn

<form method="POST">
	<table>
		<tr>
			<td>First Name:</td>
			<td><input name="firstName" type="text" value="Harry"/></td>
		</tr>
		<tr>
			<td>Last Name:</td>
			<td><input name="lastName" type="text" value="Potter"/></td>
		</tr>
		<tr>
			<td colspan="2">
				<input type="submit" value="Save Changes"/>
			</td>
		</tr>
	</table>
</form>

前面的 JSP 假设表单支持对象(form-backing object)在模型中的变量名为 command。如果你将表单支持对象以其他名称放入模型中(这无疑是一种最佳实践),你可以将表单绑定到该命名变量,如下例所示:spring-doc.cadn.net.cn

<form:form modelAttribute="user">
	<table>
		<tr>
			<td>First Name:</td>
			<td><form:input path="firstName"/></td>
		</tr>
		<tr>
			<td>Last Name:</td>
			<td><form:input path="lastName"/></td>
		</tr>
		<tr>
			<td colspan="2">
				<input type="submit" value="Save Changes"/>
			</td>
		</tr>
	</table>
</form:form>

input标签

此标签默认渲染一个带有绑定值且 input 的 HTML type='text' 元素。 有关此标签的示例,请参见表单标签。您也可以使用 HTML5 特定的类型,例如 emailteldate 等。spring-doc.cadn.net.cn

checkbox标签

此标签会渲染一个 HTML input 标签,并将其 type 属性设置为 checkboxspring-doc.cadn.net.cn

假设我们的User具有诸如订阅新闻简报和兴趣爱好列表等偏好设置。以下示例展示了Preferences类:spring-doc.cadn.net.cn

public class Preferences {

	private boolean receiveNewsletter;
	private String[] interests;
	private String favouriteWord;

	public boolean isReceiveNewsletter() {
		return receiveNewsletter;
	}

	public void setReceiveNewsletter(boolean receiveNewsletter) {
		this.receiveNewsletter = receiveNewsletter;
	}

	public String[] getInterests() {
		return interests;
	}

	public void setInterests(String[] interests) {
		this.interests = interests;
	}

	public String getFavouriteWord() {
		return favouriteWord;
	}

	public void setFavouriteWord(String favouriteWord) {
		this.favouriteWord = favouriteWord;
	}
}
class Preferences(
		var receiveNewsletter: Boolean,
		var interests: StringArray,
		var favouriteWord: String
)

相应的 form.jsp 文件可能如下所示:spring-doc.cadn.net.cn

<form:form>
	<table>
		<tr>
			<td>Subscribe to newsletter?:</td>
			<%-- Approach 1: Property is of type java.lang.Boolean --%>
			<td><form:checkbox path="preferences.receiveNewsletter"/></td>
		</tr>

		<tr>
			<td>Interests:</td>
			<%-- Approach 2: Property is of an array or of type java.util.Collection --%>
			<td>
				Quidditch: <form:checkbox path="preferences.interests" value="Quidditch"/>
				Herbology: <form:checkbox path="preferences.interests" value="Herbology"/>
				Defence Against the Dark Arts: <form:checkbox path="preferences.interests" value="Defence Against the Dark Arts"/>
			</td>
		</tr>

		<tr>
			<td>Favourite Word:</td>
			<%-- Approach 3: Property is of type java.lang.Object --%>
			<td>
				Magic: <form:checkbox path="preferences.favouriteWord" value="Magic"/>
			</td>
		</tr>
	</table>
</form:form>

checkbox 标签有三种使用方式,可以满足您所有的复选框需求。spring-doc.cadn.net.cn

  • 方法一:当绑定的值类型为 java.lang.Boolean 时,如果绑定值为 input(checkbox),则 checked 会被标记为 truevalue 属性对应于 setValue(Object) 方法所设置的值属性的解析结果。spring-doc.cadn.net.cn

  • 方法二:当绑定的值类型为 arrayjava.util.Collection 时,如果配置的 input(checkbox) 值存在于该绑定的 checked 中,则对应的 setValue(Object) 将被标记为 Collectionspring-doc.cadn.net.cn

  • 方法三:对于任何其他绑定的值类型,如果配置的 input(checkbox) 方法所设置的值等于绑定的值,则 checked 将被标记为 setValue(Object)spring-doc.cadn.net.cn

请注意,无论采用哪种方式,生成的 HTML 结构都是相同的。以下 HTML 片段定义了一些复选框:spring-doc.cadn.net.cn

<tr>
	<td>Interests:</td>
	<td>
		Quidditch: <input name="preferences.interests" type="checkbox" value="Quidditch"/>
		<input type="hidden" value="1" name="_preferences.interests"/>
		Herbology: <input name="preferences.interests" type="checkbox" value="Herbology"/>
		<input type="hidden" value="1" name="_preferences.interests"/>
		Defence Against the Dark Arts: <input name="preferences.interests" type="checkbox" value="Defence Against the Dark Arts"/>
		<input type="hidden" value="1" name="_preferences.interests"/>
	</td>
</tr>

你可能不会预料到在每个复选框后面都会看到一个额外的隐藏字段。 当 HTML 页面中的复选框未被选中时,在表单提交后,其值不会作为 HTTP 请求参数的一部分发送到服务器,因此我们需要针对 HTML 的这一特性采取变通方法,以确保 Spring 表单数据绑定能够正常工作。 checkbox 标签遵循了 Spring 现有的约定:为每个复选框包含一个以下划线(_)为前缀的隐藏参数。通过这种方式,你实际上是告诉 Spring:“该复选框在表单中是可见的,我希望表单数据绑定的目标对象能够反映该复选框的状态,无论其是否被选中。”spring-doc.cadn.net.cn

checkboxes标签

此标签会渲染多个 HTML input 标签,并将 type 设置为 checkboxspring-doc.cadn.net.cn

本节内容基于前一节 checkbox 标签示例。有时,您可能不希望在 JSP 页面中列出所有可能的兴趣爱好,而更愿意在运行时提供一个可用选项的列表,并将其传递给标签。这正是 checkboxes 标签的作用。您可以在 Array 属性中传入一个包含可用选项的 ListMapitems。通常情况下,绑定的属性是一个集合,以便能够保存用户选择的多个值。以下示例展示了一个使用该标签的 JSP 页面:spring-doc.cadn.net.cn

<form:form>
	<table>
		<tr>
			<td>Interests:</td>
			<td>
				<%-- Property is of an array or of type java.util.Collection --%>
				<form:checkboxes path="preferences.interests" items="${interestList}"/>
			</td>
		</tr>
	</table>
</form:form>

本示例假设 interestList 是一个作为模型属性可用的 List, 其中包含可供选择的字符串值。如果使用 Map, 则 map 条目的键将用作值,而 map 条目的值将用作要显示的标签。 您也可以使用自定义对象,并通过 itemValue 指定用作值的属性名, 通过 itemLabel 指定用作显示标签的属性名。spring-doc.cadn.net.cn

radiobutton标签

此标签渲染一个 HTML input 元素,并将其 type 属性设置为 radiospring-doc.cadn.net.cn

一种典型的使用模式涉及多个标签实例绑定到同一个属性,但具有不同的值,如下例所示:spring-doc.cadn.net.cn

<tr>
	<td>Sex:</td>
	<td>
		Male: <form:radiobutton path="sex" value="M"/> <br/>
		Female: <form:radiobutton path="sex" value="F"/>
	</td>
</tr>

radiobuttons标签

此标签会渲染多个 HTML input 元素,并将 type 属性设置为 radiospring-doc.cadn.net.cn

checkboxes 标签 类似,您可能希望将可用选项作为运行时变量传入。对于这种用法,您可以使用 radiobuttons 标签。您需要传入一个包含可用选项的 ArrayListMap,这些选项位于其 items 属性中。如果您使用 Map,则映射条目的键将用作值,而映射条目的值将用作要显示的标签。您还可以使用自定义对象,通过 itemValue 指定值对应的属性名,并通过 itemLabel 指定标签对应的属性名,如下例所示:spring-doc.cadn.net.cn

<tr>
	<td>Sex:</td>
	<td><form:radiobuttons path="sex" items="${sexOptions}"/></td>
</tr>

password标签

此标签会渲染一个 HTML input 标签,其类型设置为 password,并绑定相应的值。spring-doc.cadn.net.cn

<tr>
	<td>Password:</td>
	<td>
		<form:password path="password"/>
	</td>
</tr>

请注意,默认情况下密码值不会显示。如果您希望显示密码值,可以将 showPassword 属性的值设置为 true,如下例所示:spring-doc.cadn.net.cn

<tr>
	<td>Password:</td>
	<td>
		<form:password path="password" value="^76525bvHGq" showPassword="true"/>
	</td>
</tr>

select标签

此标签用于渲染一个 HTML 'select' 元素。它支持将所选选项进行数据绑定,并支持嵌套使用 optionoptions 标签。spring-doc.cadn.net.cn

假设一个 User 拥有一个技能列表。对应的 HTML 可能如下所示:spring-doc.cadn.net.cn

<tr>
	<td>Skills:</td>
	<td><form:select path="skills" items="${skills}"/></td>
</tr>

如果User’s的技能是草药学,那么“技能”行的 HTML 源代码可能如下所示:spring-doc.cadn.net.cn

<tr>
	<td>Skills:</td>
	<td>
		<select name="skills" multiple="true">
			<option value="Potions">Potions</option>
			<option value="Herbology" selected="selected">Herbology</option>
			<option value="Quidditch">Quidditch</option>
		</select>
	</td>
</tr>

option标签

此标签用于渲染一个 HTML option 元素。它会根据绑定的值设置 selected 属性。以下 HTML 展示了其典型的输出结果:spring-doc.cadn.net.cn

<tr>
	<td>House:</td>
	<td>
		<form:select path="house">
			<form:option value="Gryffindor"/>
			<form:option value="Hufflepuff"/>
			<form:option value="Ravenclaw"/>
			<form:option value="Slytherin"/>
		</form:select>
	</td>
</tr>

如果User’s的学院是格兰芬多,那么“学院”行的 HTML 源代码将如下所示:spring-doc.cadn.net.cn

<tr>
	<td>House:</td>
	<td>
		<select name="house">
			<option value="Gryffindor" selected="selected">Gryffindor</option> (1)
			<option value="Hufflepuff">Hufflepuff</option>
			<option value="Ravenclaw">Ravenclaw</option>
			<option value="Slytherin">Slytherin</option>
		</select>
	</td>
</tr>
1 注意添加了一个 selected 属性。

options标签

此标签会渲染一个 HTML option 元素列表。它会根据绑定的值设置 selected 属性。以下 HTML 展示了其典型的输出结果:spring-doc.cadn.net.cn

<tr>
	<td>Country:</td>
	<td>
		<form:select path="country">
			<form:option value="-" label="--Please Select"/>
			<form:options items="${countryList}" itemValue="code" itemLabel="name"/>
		</form:select>
	</td>
</tr>

如果User居住在英国,'Country'行的HTML源代码将如下所示:spring-doc.cadn.net.cn

<tr>
	<td>Country:</td>
	<td>
		<select name="country">
			<option value="-">--Please Select</option>
			<option value="AT">Austria</option>
			<option value="UK" selected="selected">United Kingdom</option> (1)
			<option value="US">United States</option>
		</select>
	</td>
</tr>
1 注意添加了一个 selected 属性。

如上例所示,option 标签与 options 标签结合使用时,会生成相同的标准 HTML,但允许您在 JSP 中显式指定一个仅用于显示的值(该值应属于展示层),例如示例中的默认字符串:“-- 请选择”。spring-doc.cadn.net.cn

items 属性通常由一个集合或数组形式的项目对象填充。 如果指定了 itemValueitemLabel,它们将引用这些项目对象的 bean 属性; 否则,项目对象本身将被转换为字符串。此外,你也可以指定一个 Map 类型的 items, 在这种情况下,map 的键将被解释为选项值(option values),而 map 的值则对应于选项标签(option labels)。 如果同时指定了 itemValueitemLabel(或两者都指定), 那么 itemValue 属性将作用于 map 的键,而 itemLabel 属性将作用于 map 的值。spring-doc.cadn.net.cn

textarea标签

此标签用于渲染一个 HTML textarea 元素。以下 HTML 展示了其典型的输出结果:spring-doc.cadn.net.cn

<tr>
	<td>Notes:</td>
	<td><form:textarea path="notes" rows="3" cols="20"/></td>
	<td><form:errors path="notes"/></td>
</tr>

hidden标签

此标签会渲染一个 HTML input 标签,其 type 属性设置为 hidden,并绑定相应的值。若要提交一个未绑定的隐藏值,请直接使用 HTML 的 input 标签,并将 type 设置为 hidden。 以下 HTML 展示了该标签典型的输出结果:spring-doc.cadn.net.cn

<form:hidden path="house"/>

如果我们选择将 house 值作为隐藏字段提交,HTML 代码将如下所示:spring-doc.cadn.net.cn

<input name="house" type="hidden" value="Gryffindor"/>

errors标签

此标签在 HTML span 元素中渲染字段错误。它可访问在您的控制器中创建的错误,或由与您的控制器关联的任何验证器所生成的错误。spring-doc.cadn.net.cn

假设我们希望在提交表单后,一次性显示 firstNamelastName 字段的所有错误消息。我们为 User 类的实例提供了一个名为 UserValidator 的验证器,如下例所示:spring-doc.cadn.net.cn

public class UserValidator implements Validator {

	public boolean supports(Class candidate) {
		return User.class.isAssignableFrom(candidate);
	}

	public void validate(Object obj, Errors errors) {
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "required", "Field is required.");
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "required", "Field is required.");
	}
}
class UserValidator : Validator {

	override fun supports(candidate: Class<*>): Boolean {
		return User::class.java.isAssignableFrom(candidate)
	}

	override fun validate(obj: Any, errors: Errors) {
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "required", "Field is required.")
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "required", "Field is required.")
	}
}

form.jsp 可能如下所示:spring-doc.cadn.net.cn

<form:form>
	<table>
		<tr>
			<td>First Name:</td>
			<td><form:input path="firstName"/></td>
			<%-- Show errors for firstName field --%>
			<td><form:errors path="firstName"/></td>
		</tr>

		<tr>
			<td>Last Name:</td>
			<td><form:input path="lastName"/></td>
			<%-- Show errors for lastName field --%>
			<td><form:errors path="lastName"/></td>
		</tr>
		<tr>
			<td colspan="3">
				<input type="submit" value="Save Changes"/>
			</td>
		</tr>
	</table>
</form:form>

如果我们在 firstNamelastName 字段中提交一个包含空值的表单, 生成的 HTML 将如下所示:spring-doc.cadn.net.cn

<form method="POST">
	<table>
		<tr>
			<td>First Name:</td>
			<td><input name="firstName" type="text" value=""/></td>
			<%-- Associated errors to firstName field displayed --%>
			<td><span name="firstName.errors">Field is required.</span></td>
		</tr>

		<tr>
			<td>Last Name:</td>
			<td><input name="lastName" type="text" value=""/></td>
			<%-- Associated errors to lastName field displayed --%>
			<td><span name="lastName.errors">Field is required.</span></td>
		</tr>
		<tr>
			<td colspan="3">
				<input type="submit" value="Save Changes"/>
			</td>
		</tr>
	</table>
</form>

如果我们想要显示某个页面的所有错误信息该怎么办?下面的示例展示了 errors 标签还支持一些基本的通配符功能。spring-doc.cadn.net.cn

以下示例在页面顶部显示一个错误列表,随后在各个字段旁边显示针对特定字段的错误:spring-doc.cadn.net.cn

<form:form>
	<form:errors path="*" cssClass="errorBox"/>
	<table>
		<tr>
			<td>First Name:</td>
			<td><form:input path="firstName"/></td>
			<td><form:errors path="firstName"/></td>
		</tr>
		<tr>
			<td>Last Name:</td>
			<td><form:input path="lastName"/></td>
			<td><form:errors path="lastName"/></td>
		</tr>
		<tr>
			<td colspan="3">
				<input type="submit" value="Save Changes"/>
			</td>
		</tr>
	</table>
</form:form>

HTML 内容如下所示:spring-doc.cadn.net.cn

<form method="POST">
	<span name="*.errors" class="errorBox">Field is required.<br/>Field is required.</span>
	<table>
		<tr>
			<td>First Name:</td>
			<td><input name="firstName" type="text" value=""/></td>
			<td><span name="firstName.errors">Field is required.</span></td>
		</tr>

		<tr>
			<td>Last Name:</td>
			<td><input name="lastName" type="text" value=""/></td>
			<td><span name="lastName.errors">Field is required.</span></td>
		</tr>
		<tr>
			<td colspan="3">
				<input type="submit" value="Save Changes"/>
			</td>
		</tr>
	</table>
</form>

spring-form.tld 标签库描述符(TLD)包含在 spring-webmvc.jar 中。 有关各个标签的完整参考,请浏览 API 参考文档 或查看标签库描述。spring-doc.cadn.net.cn

HTTP 方法转换

REST 的一个关键原则是使用“统一接口”。这意味着所有资源(URL)都可以使用相同的四种 HTTP 方法进行操作:GET、PUT、POST 和 DELETE。对于每个方法,HTTP 规范都定义了其确切的语义。例如,GET 请求应当始终是安全的操作,即它不应产生任何副作用;而 PUT 或 DELETE 请求应当是幂等的,即你可以反复执行这些操作,但最终结果应保持一致。虽然 HTTP 定义了这四种方法,但 HTML 仅支持其中两种:GET 和 POST。幸运的是,有两种可能的解决方法:你可以使用 JavaScript 来执行 PUT 或 DELETE 请求,或者也可以通过 POST 请求,并将“真实”的 HTTP 方法作为额外参数传递(在 HTML 表单中以隐藏输入字段的形式实现)。Spring 的 HiddenHttpMethodFilter 使用了后一种技巧。此过滤器是一个普通的 Servlet 过滤器,因此它可以与任何 Web 框架结合使用(不仅限于 Spring MVC)。将此过滤器添加到您的 Web 应用中。xml, 和一个隐藏的 method 参数被转换成相应的HTTP方法请求。spring-doc.cadn.net.cn

为了支持 HTTP 方法转换,Spring MVC 表单标签已更新以支持设置 HTTP 方法。例如,以下代码片段来自 Pet Clinic 示例:spring-doc.cadn.net.cn

<form:form method="delete">
	<p class="submit"><input type="submit" value="Delete Pet"/></p>
</form:form>

前面的示例执行了一个 HTTP POST 请求,其中“真正的” DELETE 方法通过一个请求参数隐藏起来。该参数会被在 web.xml 中定义的 HiddenHttpMethodFilter 捕获,如下例所示:spring-doc.cadn.net.cn

<filter>
	<filter-name>httpMethodFilter</filter-name>
	<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>

<filter-mapping>
	<filter-name>httpMethodFilter</filter-name>
	<servlet-name>petclinic</servlet-name>
</filter-mapping>

以下示例展示了对应的 @Controller 方法:spring-doc.cadn.net.cn

@RequestMapping(method = RequestMethod.DELETE)
public String deletePet(@PathVariable int ownerId, @PathVariable int petId) {
	this.clinic.deletePet(petId);
	return "redirect:/owners/" + ownerId;
}
@RequestMapping(method = [RequestMethod.DELETE])
fun deletePet(@PathVariable ownerId: Int, @PathVariable petId: Int): String {
	clinic.deletePet(petId)
	return "redirect:/owners/$ownerId"
}

HTML5 标签

Spring 表单标签库允许输入动态属性,这意味着您可以输入任何 HTML5 特定的属性。spring-doc.cadn.net.cn

表单的 input 标签支持输入除 text 之外的其他 type 属性。这是为了允许渲染新的 HTML5 特定输入类型,例如 emaildaterange 等。请注意,无需显式指定 type='text',因为 text 是默认类型。spring-doc.cadn.net.cn