跳到主要内容
版本:7.0.3

JSP和JSTL

Hunyuan 7b 中英对照 JSP and JSTL

Spring框架内置了将Spring MVC与JSP和JSTL结合使用的集成功能。

视图解析器

在使用JSP进行开发时,通常会声明一个InternalResourceViewResolver bean。

InternalResourceViewResolver 可用于调度到任何 Servlet 资源,但特别适用于 JSP 文件。作为最佳实践,我们强烈建议将 JSP 文件放置在 WEB-INF 目录下的某个子目录中,这样客户端就无法直接访问这些文件了。

以下配置所做的就是注册一个JSP视图解析器,该解析器使用默认的视图名称前缀“/WEB-INF/”和默认的后缀“.jsp”。

@Configuration
public class WebConfiguration implements WebMvcConfigurer {

@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp();
}
}
备注

你可以指定自定义的前缀和后缀。

JSPs与JSTL

在使用JSP标准标签库(JSTL)时,必须使用一个特殊的视图类JstlView,因为JSTL在某些功能(如I18N特性)能够正常工作之前需要一些准备工作。

Spring的JSP标签库

正如前面章节所描述的,Spring提供了将请求参数绑定到命令对象的功能。为了便于结合这些数据绑定特性来开发JSP页面,Spring提供了一些标签,使开发流程更加简便。所有Spring标签都具备HTML转义功能,可以启用或禁用字符的转义。

spring.tld 标签库描述符(TLD)包含在 spring-webmvc.jar 中。如需了解各个标签的详细信息,可以查看 API 参考文档,或直接参阅标签库的描述。

Spring的表单标签库

从2.0版本开始,Spring提供了一整套支持数据绑定的标签,用于在使用JSP和Spring Web MVC时处理表单元素。每个标签都支持其对应的HTML标签所具有的属性集合,使得这些标签的使用变得熟悉且直观。这些标签生成的HTML代码符合HTML 4.01/XHTML 1.0标准。

与其他表单/输入标签库不同,Spring的表单标签库与Spring Web MVC集成在一起,使得这些标签能够访问控制器处理的命令对象和引用数据。正如我们在以下示例中展示的那样,这些表单标签使JSP页面更易于开发、阅读和维护。

我们逐一查看这些表单标签,并通过示例来了解每个标签的用法。对于某些需要进一步解释的标签,我们还提供了生成的HTML代码片段。

配置

form标签库包含在spring-webmvc.jar中。该库的描述文件名为spring-form.tld

要使用此库中的标签,请在JSP页面的顶部添加以下指令:

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

其中 form 是你希望用于此库中标签的标签前缀名称。

Form 标签

此标签渲染一个HTML的“form”元素,并为内部标签提供绑定路径以便进行数据绑定。它将命令对象放入PageContext中,这样内部标签就可以访问该命令对象了。这个库中的所有其他标签都是“form”标签的嵌套标签。

假设我们有一个名为User的领域对象(domain object)。它是一个JavaBean,具有firstNamelastName等属性。我们可以将其用作表单控制器(form controller)的表单支撑对象(form-backing object),该控制器返回form.jsp文件。以下示例展示了form.jsp可能的外观:

<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 的值是从页面控制器(page controller)放入 PageContext 中的命令对象(command object)中获取的。继续阅读,以了解如何更复杂地使用 form 标签与内部标签(inner tags)结合的示例。

以下列表显示了生成的HTML代码,它看起来像一个标准表单:

<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假设表单支持对象的变量名为command。如果你将表单支持对象以另一个名称放入模型中(这绝对是一种最佳实践),你可以将该表单绑定到这个命名的变量上,如下例所示:

<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 标签

该标签会渲染一个HTML input 元素,默认情况下该元素的值为绑定值(bound value),类型为 text。有关此标签的示例,请参阅 表单标签。您还可以使用HTML5特有的输入类型,如 emailteldate 等。

checkbox 标签

此标签会渲染一个HTML input 标签,并将 type 属性设置为 checkbox

假设我们的User(用户)有一些偏好设置,比如订阅时事通讯和拥有一系列爱好。以下示例展示了Preferences(偏好设置)类:

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

相应的form.jsp文件可以如下所示:

<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 标签有三种用法,应该能够满足您所有的复选框需求。

  • 方法一:当绑定值为 java.lang.Boolean 类型时,如果绑定值为 true,则 input(checkbox) 会被标记为选中状态。value 属性对应于 setValue(Object) 属性解析后的值。

  • 方法二:当绑定值为 arrayjava.util.Collection 类型时,如果配置的 setValue(Object) 值存在于绑定的 Collection 中,则 input(checkbox) 会被标记为选中状态。

  • 方法三:对于其他任何类型的绑定值,如果配置的 setValue(Object) 与绑定值相等,则 input(checkbox) 会被标记为选中状态。

请注意,无论采用哪种方法,都会生成相同的HTML结构。以下HTML代码片段定义了一些复选框:

<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请求参数的一部分被发送到服务器。因此,为了使Spring表单数据绑定能够正常工作,我们需要解决HTML中的这一特殊问题。checkbox标签遵循了Spring的现有规范,为每个复选框添加一个以下划线(_)开头的隐藏参数。通过这种方式,你实际上是在告诉Spring:“这个复选框在表单中是可见的,无论怎样,我都希望绑定表单数据的对象能够反映出该复选框的状态。”

checkboxes 标签

此标签会渲染多个HTML input 标签,其 type 属性被设置为 checkbox

本节基于之前“checkbox”标签部分的示例进行扩展。有时候,你可能不希望在JSP页面中列出所有可能的爱好选项。你更希望能够在运行时提供可用的选项列表,并将其传递给该标签。这就是“checkboxes”标签的用途。你可以通过“items”属性传递一个包含可用选项的“Array”、“List”或“Map”。通常,绑定属性是一个集合(collection),以便它可以容纳用户选择的多个值。以下示例展示了一个使用此标签的JSP:

<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,则映射的键将用作值,而映射的值将用作显示的标签。您还可以使用自定义对象,在该对象中,您可以通过 itemValue 提供值的属性名称,通过 itemLabel 提供标签。

radiobutton 标签

此标签会渲染一个HTML input元素,其type属性被设置为radio

一种典型的使用模式涉及多个标签实例绑定到相同的属性上,但具有不同的值,如下例所示:

<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属性被设置为radio

checkboxes标签一样,您可能希望将可用的选项作为运行时变量传递进来。对于这种用法,您可以使用radiobuttons标签。您可以通过items属性传递一个包含可用选项的ArrayListMap。如果您使用的是Map,则映射的键将作为值显示,而映射的值将作为要显示的标签。您还可以使用自定义对象,在该对象中,您可以使用itemValue提供值的属性名称,使用itemLabel提供标签的属性名称,如下例所示:

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

password 标签

此标签会渲染一个HTML的<input>元素,其类型被设置为password(密码输入),并绑定相应的值。

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

请注意,默认情况下,密码值不会显示。如果您确实希望显示密码值,可以像下面的例子所示,将showPassword属性的值设置为true

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

select 标签

此标签用于渲染HTML的“select”元素。它支持与所选选项的数据绑定,同时也支持使用嵌套的“option”和“options”标签。

假设一个User(用户)有一系列技能。相应的HTML代码可能如下所示:

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

如果用户的技能是“草药学”,那么“技能”这一行的HTML源代码可能如下所示:

<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展示了该标签的典型输出效果:

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

如果“用户”的学院属于格兰芬多(Gryffindor),那么‘学院’这一行的HTML代码源将会如下所示:

<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>
  • 注意添加了 selected 属性。

options 标签

此标签会渲染一个HTML option元素列表。它会根据绑定的值来设置selected属性。以下HTML代码展示了其典型的输出效果:

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

如果“用户”居住在英国,那么“国家”这一行的HTML源代码将如下所示:

<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>
  • 注意新增了一个 selected 属性。

如前面的例子所示,将option标签与options标签结合使用可以生成相同的标准HTML,但允许你在JSP中明确指定仅用于显示的值(即该值出现的位置),例如例子中的默认字符串:“-- 请选择”。

items属性通常填充有一组项目对象(item objects)的集合或数组。如果指定了itemValueitemLabel,它们就分别对应这些项目对象的bean属性;否则,这些项目对象本身会被转换为字符串。另外,你也可以指定一个Map来存储项目信息,在这种情况下,Map的键会被解释为选项值(option values),而Map的值则对应于选项标签(option labels)。如果同时指定了itemValueitemLabel(或两者都有),那么itemValue属性将应用于Map的键,而itemLabel属性将应用于Map的值。

textarea 标签

该标签用于渲染一个HTML的textarea元素。以下HTML代码展示了其典型的输出效果:

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

hidden 标签

此标签会渲染一个type属性设置为hidden的HTML input标签,并绑定相应的值。如果要提交一个未绑定的隐藏值,也可以使用type属性设置为hidden的HTML input标签。以下HTML代码展示了其典型的输出效果:

<form:hidden path="house"/>

如果我们选择将house值作为隐藏字段提交,HTML代码将如下所示:

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

errors 标签

此标签会在HTML的span元素中渲染字段错误。它允许访问在控制器中生成的错误,或是与控制器关联的任何验证器所生成的错误。

假设我们希望在提交表单后显示firstName(名字)和lastName(姓氏)字段的所有错误信息。我们有一个针对User类实例的验证器,名为UserValidator,如下例所示:

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

form.jsp 可以如下所示:

<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将会如下所示:

<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标签还支持一些基本的通配符功能。

  • path="*":显示所有错误。
  • path="lastName":显示与lastName字段相关的所有错误。
  • 如果省略了path,则只显示对象错误。

以下示例在页面顶部显示错误列表,接着在各个字段旁边显示该字段特有的错误:

<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代码如下:

<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 参考文档,或查看该标签库的描述。

HTTP方法转换

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

为了支持HTTP方法转换,Spring MVC的表单标签(form tag)已经更新为可以设置HTTP方法。例如,以下代码片段来自“宠物诊所”(Pet Clinic)示例:

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

前面的示例执行了一个HTTP POST请求,而真正的DELETE方法被隐藏在一个请求参数后面。这个请求参数会被HiddenHttpMethodFilter捕获,该过滤器在web.xml中有所定义,如下例所示:

<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 方法:

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

HTML5 标签

Spring Form 标签库允许输入动态属性,这意味着你可以输入任何 HTML5 特有的属性。

input 标签支持输入除 text 之外的其他类型属性。这样就可以渲染新的 HTML5 特定输入类型,如 emaildaterange 等。需要注意的是,不一定要输入 type='text',因为 text 是默认类型。