For the latest stable version, please use Spring Framework 6.2.1! |
JSP and JSTL
The Spring Framework has a built-in integration for using Spring MVC with JSP and JSTL.
View Resolvers
When developing with JSPs, you typically declare an InternalResourceViewResolver
bean.
InternalResourceViewResolver
can be used for dispatching to any Servlet resource but in
particular for JSPs. As a best practice, we strongly encourage placing your JSP files in
a directory under the 'WEB-INF'
directory so there can be no direct access by clients.
<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>
JSPs versus JSTL
When using the JSP Standard Tag Library (JSTL) you must use a special view class, the
JstlView
, as JSTL needs some preparation before things such as the I18N features can
work.
Spring’s JSP Tag Library
Spring provides data binding of request parameters to command objects, as described in earlier chapters. To facilitate the development of JSP pages in combination with those data binding features, Spring provides a few tags that make things even easier. All Spring tags have HTML escaping features to enable or disable escaping of characters.
The spring.tld
tag library descriptor (TLD) is included in the spring-webmvc.jar
.
For a comprehensive reference on individual tags, browse the
API reference
or see the tag library description.
Spring’s form tag library
As of version 2.0, Spring provides a comprehensive set of data binding-aware tags for handling form elements when using JSP and Spring Web MVC. Each tag provides support for the set of attributes of its corresponding HTML tag counterpart, making the tags familiar and intuitive to use. The tag-generated HTML is HTML 4.01/XHTML 1.0 compliant.
Unlike other form/input tag libraries, Spring’s form tag library is integrated with Spring Web MVC, giving the tags access to the command object and reference data your controller deals with. As we show in the following examples, the form tags make JSPs easier to develop, read, and maintain.
We go through the form tags and look at an example of how each tag is used. We have included generated HTML snippets where certain tags require further commentary.
Configuration
The form tag library comes bundled in spring-webmvc.jar
. The library descriptor is
called spring-form.tld
.
To use the tags from this library, add the following directive to the top of your JSP page:
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
where form
is the tag name prefix you want to use for the tags from this library.
The Form Tag
This tag renders an HTML 'form' element and exposes a binding path to inner tags for
binding. It puts the command object in the PageContext
so that the command object can
be accessed by inner tags. All the other tags in this library are nested tags of the
form
tag.
Assume that we have a domain object called User
. It is a JavaBean with properties
such as firstName
and lastName
. We can use it as the form-backing object of our
form controller, which returns form.jsp
. The following example shows what form.jsp
could
look like:
<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>
The firstName
and lastName
values are retrieved from the command object placed in
the PageContext
by the page controller. Keep reading to see more complex examples of
how inner tags are used with the form
tag.
The following listing shows the generated HTML, which looks like a standard form:
<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>
The preceding JSP assumes that the variable name of the form-backing object is
command
. If you have put the form-backing object into the model under another name
(definitely a best practice), you can bind the form to the named variable, as the
following example shows:
<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>
The input
Tag
This tag renders an HTML input
element with the bound value and type='text'
by default.
For an example of this tag, see The Form Tag. You can also use
HTML5-specific types, such as email
, tel
, date
, and others.
The checkbox
Tag
This tag renders an HTML input
tag with the type
set to checkbox
.
Assume that our User
has preferences such as newsletter subscription and a list of
hobbies. The following example shows the Preferences
class:
-
Java
-
Kotlin
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
)
The corresponding form.jsp
could then resemble the following:
<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>
There are three approaches to the checkbox
tag, which should meet all your checkbox needs.
-
Approach One: When the bound value is of type
java.lang.Boolean
, theinput(checkbox)
is marked aschecked
if the bound value istrue
. Thevalue
attribute corresponds to the resolved value of thesetValue(Object)
value property. -
Approach Two: When the bound value is of type
array
orjava.util.Collection
, theinput(checkbox)
is marked aschecked
if the configuredsetValue(Object)
value is present in the boundCollection
. -
Approach Three: For any other bound value type, the
input(checkbox)
is marked aschecked
if the configuredsetValue(Object)
is equal to the bound value.
Note that, regardless of the approach, the same HTML structure is generated. The following HTML snippet defines some checkboxes:
<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>
You might not expect to see the additional hidden field after each checkbox.
When a checkbox in an HTML page is not checked, its value is not sent to the
server as part of the HTTP request parameters once the form is submitted, so we need a
workaround for this quirk in HTML for Spring form data binding to work. The
checkbox
tag follows the existing Spring convention of including a hidden parameter
prefixed by an underscore (_
) for each checkbox. By doing this, you are effectively
telling Spring that “the checkbox was visible in the form, and I want my object to
which the form data binds to reflect the state of the checkbox, no matter what.”
The checkboxes
Tag
This tag renders multiple HTML input
tags with the type
set to checkbox
.
This section build on the example from the previous checkbox
tag section. Sometimes, you prefer
not to have to list all the possible hobbies in your JSP page. You would rather provide
a list at runtime of the available options and pass that in to the tag. That is the
purpose of the checkboxes
tag. You can pass in an Array
, a List
, or a Map
that contains
the available options in the items
property. Typically, the bound property is a
collection so that it can hold multiple values selected by the user. The following example
shows a JSP that uses this tag:
<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>
This example assumes that the interestList
is a List
available as a model attribute
that contains strings of the values to be selected from. If you use a Map
,
the map entry key is used as the value, and the map entry’s value is used as
the label to be displayed. You can also use a custom object where you can provide the
property names for the value by using itemValue
and the label by using itemLabel
.
The radiobutton
Tag
This tag renders an HTML input
element with the type
set to radio
.
A typical usage pattern involves multiple tag instances bound to the same property but with different values, as the following example shows:
<tr>
<td>Sex:</td>
<td>
Male: <form:radiobutton path="sex" value="M"/> <br/>
Female: <form:radiobutton path="sex" value="F"/>
</td>
</tr>
The radiobuttons
Tag
This tag renders multiple HTML input
elements with the type
set to radio
.
As with the checkboxes
tag, you might want to
pass in the available options as a runtime variable. For this usage, you can use the
radiobuttons
tag. You pass in an Array
, a List
, or a Map
that contains the
available options in the items
property. If you use a Map
, the map entry key is
used as the value and the map entry’s value are used as the label to be displayed.
You can also use a custom object where you can provide the property names for the value
by using itemValue
and the label by using itemLabel
, as the following example shows:
<tr>
<td>Sex:</td>
<td><form:radiobuttons path="sex" items="${sexOptions}"/></td>
</tr>
The password
Tag
This tag renders an HTML input
tag with the type set to password
with the bound value.
<tr>
<td>Password:</td>
<td>
<form:password path="password"/>
</td>
</tr>
Note that, by default, the password value is not shown. If you do want the
password value to be shown, you can set the value of the showPassword
attribute to
true
, as the following example shows:
<tr>
<td>Password:</td>
<td>
<form:password path="password" value="^76525bvHGq" showPassword="true"/>
</td>
</tr>
The select
Tag
This tag renders an HTML 'select' element. It supports data binding to the selected
option as well as the use of nested option
and options
tags.
Assume that a User
has a list of skills. The corresponding HTML could be as follows:
<tr>
<td>Skills:</td>
<td><form:select path="skills" items="${skills}"/></td>
</tr>
If the User’s
skill are in Herbology, the HTML source of the 'Skills' row could be
as follows:
<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>
The option
Tag
This tag renders an HTML option
element. It sets selected
, based on the bound
value. The following HTML shows typical output for it:
<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>
If the User’s
house was in Gryffindor, the HTML source of the 'House' row would be
as follows:
<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 | Note the addition of a selected attribute. |
The options
Tag
This tag renders a list of HTML option
elements. It sets the selected
attribute,
based on the bound value. The following HTML shows typical output for it:
<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>
If the User
lived in the UK, the HTML source of the 'Country' row would be as follows:
<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 | Note the addition of a selected attribute. |
As the preceding example shows, the combined usage of an option
tag with the options
tag
generates the same standard HTML but lets you explicitly specify a value in the
JSP that is for display only (where it belongs), such as the default string in the
example: "-- Please Select".
The items
attribute is typically populated with a collection or array of item objects.
itemValue
and itemLabel
refer to bean properties of those item objects, if
specified. Otherwise, the item objects themselves are turned into strings. Alternatively,
you can specify a Map
of items, in which case the map keys are interpreted as option
values and the map values correspond to option labels. If itemValue
or itemLabel
(or both)
happen to be specified as well, the item value property applies to the map key, and
the item label property applies to the map value.
The textarea
Tag
This tag renders an HTML textarea
element. The following HTML shows typical output for it:
<tr>
<td>Notes:</td>
<td><form:textarea path="notes" rows="3" cols="20"/></td>
<td><form:errors path="notes"/></td>
</tr>
The hidden
Tag
This tag renders an HTML input
tag with the type
set to hidden
with the bound value. To submit
an unbound hidden value, use the HTML input
tag with the type
set to hidden
.
The following HTML shows typical output for it:
<form:hidden path="house"/>
If we choose to submit the house
value as a hidden one, the HTML would be as follows:
<input name="house" type="hidden" value="Gryffindor"/>
The errors
Tag
This tag renders field errors in an HTML span
element. It provides access to the errors
created in your controller or those that were created by any validators associated with
your controller.
Assume that we want to display all error messages for the firstName
and lastName
fields once we submit the form. We have a validator for instances of the User
class
called UserValidator
, as the following example shows:
-
Java
-
Kotlin
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.")
}
}
The form.jsp
could be as follows:
<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>
If we submit a form with empty values in the firstName
and lastName
fields,
the HTML would be as follows:
<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>
What if we want to display the entire list of errors for a given page? The next example
shows that the errors
tag also supports some basic wildcarding functionality.
-
path="*"
: Displays all errors. -
path="lastName"
: Displays all errors associated with thelastName
field. -
If
path
is omitted, only object errors are displayed.
The following example displays a list of errors at the top of the page, followed by field-specific errors next to the fields:
<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>
The HTML would be as follows:
<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>
The spring-form.tld
tag library descriptor (TLD) is included in the spring-webmvc.jar
.
For a comprehensive reference on individual tags, browse the
API reference
or see the tag library description.
HTTP Method Conversion
A key principle of REST is the use of the “Uniform Interface”. This means that all
resources (URLs) can be manipulated by using the same four HTTP methods: GET, PUT, POST,
and DELETE. For each method, the HTTP specification defines the exact semantics. For
instance, a GET should always be a safe operation, meaning that it has no side effects,
and a PUT or DELETE should be idempotent, meaning that you can repeat these operations
over and over again, but the end result should be the same. While HTTP defines these
four methods, HTML only supports two: GET and POST. Fortunately, there are two possible
workarounds: you can either use JavaScript to do your PUT or DELETE, or you can do a POST
with the “real” method as an additional parameter (modeled as a hidden input field in an
HTML form). Spring’s HiddenHttpMethodFilter
uses this latter trick. This
filter is a plain Servlet filter and, therefore, it can be used in combination with any
web framework (not just Spring MVC). Add this filter to your web.xml, and a POST
with a hidden method
parameter is converted into the corresponding HTTP method
request.
To support HTTP method conversion, the Spring MVC form tag was updated to support setting the HTTP method. For example, the following snippet comes from the Pet Clinic sample:
<form:form method="delete">
<p class="submit"><input type="submit" value="Delete Pet"/></p>
</form:form>
The preceding example performs an HTTP POST, with the “real” DELETE method hidden behind
a request parameter. It is picked up by the HiddenHttpMethodFilter
, which is defined in
web.xml, as the following example shows:
<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>
The following example shows the corresponding @Controller
method:
-
Java
-
Kotlin
@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 Tags
The Spring form tag library allows entering dynamic attributes, which means you can enter any HTML5 specific attributes.
The form input
tag supports entering a type attribute other than text
. This is
intended to allow rendering new HTML5 specific input types, such as email
, date
,
range
, and others. Note that entering type='text'
is not required, since text
is the default type.