Controllers provide access to the application behavior that you typically define through a service interface. Controllers interpret user input and transform it into a model that is represented to the user by the view. Spring implements a controller in a very abstract way, which enables you to create a wide variety of controllers.
Spring 2.5 introduced an annotation-based programming model for MVC
controllers that uses annotations such as
@RequestMapping
,
@RequestParam
,
@ModelAttribute
, and so on. This annotation
support is available for both Servlet MVC and Portlet MVC. Controllers
implemented in this style do not have to extend specific base classes or
implement specific interfaces. Furthermore, they do not usually have
direct dependencies on Servlet or Portlet APIs, although you can easily
configure access to Servlet or Portlet facilities.
Tip | |
---|---|
The Spring distribution ships with the
PetClinic sample, a web application that leverages
the annotation support described in this section, in the context of
simple form processing. The PetClinic application
resides in the |
@Controller public class HelloWorldController { @RequestMapping("/helloWorld") public ModelAndView helloWorld() { ModelAndView mac = new ModelAndView(); mav.setViewName("helloWorld"); mav.addObject("message", "Hello World!"); return mav; } }
As you can see, the @Controller
and
@RequestMapping
annotations allow flexible
method names and signatures. In this particular example the method has no
parameters and returns a ModelAndView
, but various
other (and better) strategies exist, as
are explained later in this section. ModelAndView
,
@Controller
, and
@RequestMapping
form the basis for the
Spring MVC implementation. This section documents these annotations and
how they are most commonly used in a Servlet environment.
The @Controller
annotation
indicates that a particular class serves the role of a
controller. Spring does not require you to extend
any controller base class or reference the Servlet API. However, you can
still reference Servlet-specific features if you need to do so.
The @Controller
annotation acts as
a stereotype for the annotated class, indicating its role. The
dispatcher scans such annotated classes for mapped methods and detects
@RequestMapping
annotations (see the next
section).
You can define annotated controller beans explicitly, using a
standard Spring bean definition in the dispatcher's context. However,
the @Controller
stereotype also allows
for autodetection, aligned with Spring general support for detecting
component classes in the classpath and auto-registering bean definitions
for them.
To enable autodetection of such annotated controllers, you add component scanning to your configuration. Use the spring-context schema as shown in the following XML snippet:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="org.springframework.samples.petclinic.web"/> // ... </beans>
You use the @RequestMapping
annotation to map URLs such as /appointments
onto
an entire class or a particular handler method. You can use it to
annotate both a class and a method. Typically the class-level annotation
maps a specific request path (or path pattern) onto a form controller,
with additional method-level annotations narrowing the primary mapping
for a specific HTTP method request method ("GET"/"POST") or specific
HTTP request parameters.
The following example shows a controller from the PetClinic sample application that uses this annotation:
@Controller @RequestMapping("/appointments") public class AppointmentsController { private AppointmentBook appointmentBook; @Autowired public AppointmentsController(AppointmentBook appointmentBook) { this.appointmentBook = appointmentBook; } @RequestMapping(method = RequestMethod.GET) public Appointments get() { return appointmentBook.getAppointmentsForToday(); } @RequestMapping(value="/{day}", method = RequestMethod.GET) public void getForDay(@PathVariable Date day, ExternalContext context) { Appointments appts = appointmentBook.getAppointmentsForDay(day); context.getModel().addAttribute(appts); context.selectView("appointments"); if (context.isAjaxRequest()) { //could activate a ViewHelper for component associated with main context.renderFragment("main"); } } @RequestMapping(value="/new", method = RequestMethod.GET) public AppointmentForm getNewForm() { return new AppointmentForm(); } @RequestMapping(method = RequestMethod.POST) public String post(AppointmentForm form) { appointmentBook.createAppointment(form); return "redirect:/appointments"; } }
In the example, the @RequestMapping
is used in a number of places. The first usage is on the type (class)
level, which indicates that all handling methods on this controller are
relative to the /appointments
path. The
get()
method has a further
@RequestMapping
refinement: it only
accepts GET requests, meaning that an HTTP GET for
/appointments
invokes this method. The
post()
has a similar refinement, and the
getNewForm()
combines the definition of HTTP
method and path into one, so that GET requests for
appointments/new
are handled by that method.
The getForDay()
method shows another
usage of @RequestMapping
: URI templates.
(See the next
section ).
A @RequestMapping
on the class
level is not required. Without it, all paths are simply absolute, and
not relative. The following example from the PetClinic sample
application shows a multi-action controller using
@RequestMapping
:
@Controller public class ClinicController { private final Clinic clinic; @Autowired public ClinicController(Clinic clinic) { this.clinic = clinic; } @RequestMapping("/") public void welcomeHandler() { } @RequestMapping("/vets") public ModelMap vetsHandler() { return new ModelMap(this.clinic.getVets()); } }
To access parts of a request URL in your handling methods, use
the URI templates in the
@RequestMapping
path value.
You use the @PathVariable
method
parameter annotation to indicate that a method parameter should be
bound to the value of a URI template variable.
The following code snippet shows the use of a single
@PathVariable
in a controller
method:
@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET) public String findOwner(@PathVariable String ownerId, Model model) { Owner owner = ownerService.findOwner(ownerId); model.addAttribute("owner", owner); return "displayOwner"; }
The URI Template "/owners/{ownerId}
"
specifies the variable name ownerId. When the
controller handles this request, the value of
ownerId is set to the value in the request URI.
For example, when a request comes in for /owners/fred, the value fred
is bound to the method parameter String
ownerId
.
The matching of method parameter names to URI Template variable names can only be done if your code is compiled with debugging enabled. If you do have not debugging enabled, you must specify the name of the URI Template variable name in the @PathVariable annotation in order to bind the resovled value of the variable name to a method parameter. For example:
@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET) public String findOwner(@PathVariable("ownerId") String ownerId, Model model) { // implementation omitted }
so you can also use a controller method with the following signature:
@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET) public String findOwner(@PathVariable("ownerId") String theOwner, Model model) { // implementation omitted }
You can use multiple @PathVariable annotations to bind to multiple URI Template variables:
@RequestMapping(value="/owners/{ownerId}/pets/{petId}", method=RequestMethod.GET) public String findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { Owner owner = ownerService.findOwner(ownderId); Pet pet = owner.getPet(petId); model.addAttribute("pet", pet); return "displayPet"; }
The following code snippet shows the use of path variables on a
relative path, so that the findPet()
method
will be invoked for /owners/42/pets/21
, for
instance.
@Controller @RequestMapping("/owners/{ownerId}") public class RelativePathUriTemplateController { @RequestMapping("/pets/{petId}") public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { // implementation omitted } }
Tip | |
---|---|
Method parameters that are decorated with the
|
In addition to URI templates, the
@RequestMapping
annotation also
supports Ant-style path patterns (for example,
/myPath/*.do
). A combination of URI templates and
Ant-style globs is also supported (for example,
/owners/*/pets/{petId}
).
The handler method names are taken into account for narrowing if
no path was specified explicitly, according to the specified
org.springframework.web.servlet.mvc.multiaction.MethodNameResolver
(by default an
org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver
).
This only applies if annotation mappings do not specify a path mapping
explicitly. In other words, the method name is only used for narrowing
among a set of matching methods; it does not constitute a primary path
mapping itself.
If you have a single default method (without explicit path mapping), then all requests without a more specific mapped method found are dispatched to it. If you have multiple such default methods, then the method name is taken into account for choosing between them.
You can narrow path mappings through parameter conditions: a sequence of "myParam=myValue" style expressions, with a request only mapped if each such parameter is found to have the given value. For example:
@Controller @RequestMapping("/owners/{ownerId}") public class RelativePathUriTemplateController { @RequestMapping(value = "/pets/{petId}", params="myParam=myValue") public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { // implementation omitted } }
"myParam" style expressions are also supported, with such parameters having to be present in the request (allowed to have any value). Finally, "!myParam" style expressions indicate that the specified parameter is not supposed to be present in the request.
Similarly, path mappings can be narrowed down through header conditions:
@Controller @RequestMapping("/owners/{ownerId}") public class RelativePathUriTemplateController { @RequestMapping(value = "/pets", method = RequestMethod.POST, headers="content-type=text/*") public void addPet(Pet pet, @PathVariable String ownerId) { // implementation omitted } }
In the above example, the addPet
is
only invoked when the content-type is in the text/*
range, for example, text/xml
.
Handler methods that are annotated with
@RequestMapping
can have very flexible
signatures. They may have arguments of the following types, in
arbitrary order. (except for validation results, which need to follow
right after the corresponding command object, if desired):
Request and/or response objects (Servlet API). Choose any
specific request/response type, for example,
ServletRequest
/
HttpServletRequest
.
Session object (Servlet API): of type
HttpSession
. An argument of this
type enforces the presence of a corresponding session. As a
consequence, such an argument is never
null
.
Note | |
---|---|
Session access may not be thread-safe, in particular in
a Servlet environment: Consider switching the
|
org.springframework.web.context.request.WebRequest
or
org.springframework.web.context.request.NativeWebRequest
.
Allows for generic request parameter access as well as
request/session attribute access, without ties to the native
Servlet/Portlet API.
java.util.Locale
for the current
request locale, determined by the most specific locale resolver
available, in effect, the configured
LocaleResolver
in a Servlet
environment.
java.io.InputStream
/
java.io.Reader
for access to the
request's content. This value is the raw InputStream/Reader as
exposed by the Servlet API.
java.io.OutputStream
/
java.io.Writer
for generating the
response's content. This value is the raw OutputStream/Writer as
exposed by the Servlet API.
@PathVariabe
annotated parameters
for access to URI template variables. See Section 15.3.2.1, “URI Templates”.
@RequestParam
annotated parameters
for access to specific Servlet request parameters. Parameter
values are converted to the declared method argument type. See
Section 15.3.2.4, “Binding request parameters to method parameters with
@RequestParam”.
@RequestHeader
annotated parameters
for access to specific Servlet request HTTP headers. Parameter
values are converted to the declared method argument
type.
@RequestBody
annotated parameters
for access to the request HTTP body. Parameter values are
converted to the declared method argument type using
HttpMessageConverter
s. See Section 15.3.2.5, “Mapping the request body with the @RequestBody
annotation”.
java.util.Map
/
org.springframework.ui.Model
/
org.springframework.ui.ModelMap
for
enriching the implicit model that is exposed to the web
view.
Command or form objects to bind parameters to: as bean
properties or fields, with
customizable type conversion, depending on
@InitBinder
methods and/or the
HandlerAdapter configuration. See the
webBindingInitializer
property on
AnnotationMethodHandlerAdapter
. Such
command objects along with their validation results will be
exposed as model attributes by default., using the non-qualified
command class name in property notation. For
example, "orderAddress" for type "mypackage.OrderAddress".
Specify a parameter-level ModelAttribute
annotation for declaring a specific model attribute name.
org.springframework.validation.Errors
/
org.springframework.validation.BindingResult
validation results for a preceding command or form object (the
immediately preceding argument).
org.springframework.web.bind.support.SessionStatus
status handle for marking form processing as complete, which
triggers the cleanup of session attributes that have been
indicated by the @SessionAttributes
annotation at the handler type level.
The following return types are supported for handler methods:
A ModelAndView
object, with the
model implicitly enriched with command objects and the results
of @ModelAttribute
annotated reference data
accessor methods.
A Model
object, with the
view name implicitly determined through a
RequestToViewNameTranslator
and
the model implicitly enriched with command objects and the
results of @ModelAttribute
annotated
reference data accessor methods.
A Map
object for exposing a
model, with the view name implicitly determined through a
RequestToViewNameTranslator
and
the model implicitly enriched with command objects and the
results of @ModelAttribute
annotated
reference data accessor methods.
A View
object, with the
model implicitly determined through command objects and
@ModelAttribute
annotated reference data
accessor methods. The handler method may also programmatically
enrich the model by declaring a
Model
argument (see above).
A String
value that is interpreted
as the view name, with the model implicitly determined through
command objects and @ModelAttribute
annotated
reference data accessor methods. The handler method may also
programmatically enrich the model by declaring a
Model
argument (see
above).
void
if the method handles the response
itself (by writing the response content directly, declaring an
argument of type ServletResponse
/ HttpServletResponse
for that
purpose) or if the view name is supposed to be implicitly
determined through a
RequestToViewNameTranslator
(not
declaring a response argument in the handler method
signature).
If the method is annotated with
@ResponseBody
, the return type is
written to the response HTTP body. The return value will be
converted to the declared method argument type using
HttpMessageConverter
s. See Section 15.3.2.6, “Mapping the response body with the @ResponseBody
annotation”.
Any other return type is considered as single model
attribute to be exposed to the view, using the attribute name
specified through @ModelAttribute
at the
method level (or the default attribute name based on the return
type class name). The model is implicitly enriched with command
objects and the results of @ModelAttribute
annotated reference data accessor methods.
Use the @RequestParam
annotation to bind
request parameters to a method parameter in your controller.
The following code snippet shows the usage:
@Controller @RequestMapping("/pets") @SessionAttributes("pet") public class EditPetForm { // ... @RequestMapping(method = RequestMethod.GET) public String setupForm(@RequestParam("petId") int petId, ModelMap model) { Pet pet = this.clinic.loadPet(petId); model.addAttribute("pet", pet); return "petForm"; } // ...
Parameters using this annotation are required by default, but
you can specify that a parameter is optional by setting
@RequestParam
's
required
attribute to false
(e.g., @RequestParam(value="id",
required="false")
).
The @RequestBody
method parameter
annotation indicates that a method parameter should be bound to the
value of the HTTP request body. For example:
@RequestMapping(value = "/something", method = RequestMethod.PUT) public void handle(@RequestBody String body, Writer writer) throws IOException { writer.write(body); }
You convert the request body to the method argument by using a
HttpMessageConverter
.
HttpMessageConverter
is responsible for
converting from the HTTP request message to an object and converting
from an object to the HTTP response body.
DispatcherServlet
supports annotation based
processing using the
DefaultAnnotationHandlerMapping
and
AnnotationMethodHandlerAdapter
. In Spring 3.0
the AnnotationMethodHandlerAdapter
is extended
to support the @RequestBody
and has the
following HttpMessageConverters
registered by default:
ByteArrayHttpMessageConverter
converts byte arrays.
StringHttpMessageConverter
converts
strings.
FormHttpMessageConverter
converts
form data to/from a MultiValueMap<String, String>.
SourceHttpMessageConverter
converts
to/from a javax.xml.transform.Source.
MarshallingHttpMessageConverter
converts to/from an object using the
org.springframework.oxm
package.
For more information on these converters, see Message Converters.
The MarshallingHttpMessageConverter
requires a Marshaller
and
Unmarshaller
from the
org.springframework.oxm
package to be
configured on an instance of
AnnotationMethodHandlerAdapter
in the
application context. For example:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <util:list id="beanList"> <ref bean="stringHttpMessageConverter"/> <ref bean="marshallingHttpMessageConverter"/> </util:list> </property </bean> <bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter"/> <bean id="marshallingHttpMessageConverter" class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter"> <property name="marshaller" ref="castorMarshaller" /> <property name="unmarshaller" ref="castorMarshaller" /> </bean> <bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller"/>
The @ResponseBody
annotation is
similar to @RequestBody
. This
annotation can be put on a method and
indicates that the return type should be written straight to the HTTP
response body (and not placed in a Model, or interpreted as a view
name). For example:
@RequestMapping(value = "/something", method = RequestMethod.PUT) @ResponseBody public String helloWorld() { return "Hello World"; }
The example will result in the text Hello
World
being written to the HTTP response stream.
As with @RequestBody
, Spring converts
the returned object to a response body by using a
HttpMessageConverter
. For more
information on these converters, see the previous section and Message Converters.
@ModelAttribute
has two usage scenarios
in controllers. When you map it to a
method parameter, @ModelAttribute
maps a model
attribute to the specific, annotated method parameter (see the
processSubmit()
method below). This is how the
controller gets a reference to the object holding the data entered in
the form.
You can also use the @ModelAttribute
at
the method level to provide reference data for
the model (see the populatePetTypes()
method, as in
the following example. For this usage the method signature can contain
the same types as documented previously for the
@RequestMapping
annotation.
Note | |
---|---|
|
The following code snippet shows these two usages of this annotation:
@Controller @RequestMapping("/owners/{ownerId}/pets/{petId}/edit") @SessionAttributes("pet") public class EditPetForm { // ... @ModelAttribute("types") public Collection<PetType> populatePetTypes() { return this.clinic.getPetTypes(); } @RequestMapping(method = RequestMethod.POST) public String processSubmit( @ModelAttribute("pet") Pet pet, BindingResult result, SessionStatus status) { new PetValidator().validate(pet, result); if (result.hasErrors()) { return "petForm"; } else { this.clinic.storePet(pet); status.setComplete(); return "redirect:owner.do?ownerId=" + pet.getOwner().getId(); } } }
The type-level @SessionAttributes
annotation declares session attributes used by a specific handler.
This will typically list the names of model attributes which should be
transparently stored in the session or some conversational storage,
serving as form-backing beans between subsequent requests.
The following code snippet shows the usage of this annotation:
@Controller @RequestMapping("/editPet.do") @SessionAttributes("pet") public class EditPetForm { // ... }
The @CookieValue
annotation
allows a method parameter to be bound to the value of an HTTP
cookie.
Let us consider that the following cookie has been received with an http request:
JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84
The following code sample allows you to easily get the value of the "JSESSIONID"cookie:
@RequestMapping("/displayHeaderInfo.do") public void displayHeaderInfo(@CookieValue("JSESSIONID") String cookie) { //... }
This annotation is supported for annotated handler methods in Servlet and Portlet environments.
The @RequestHeader
annotation
allows a method parameter to be bound to a request header.
Here is a request header sample:
Host localhost:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9 Accept-Language fr,en-gb;q=0.7,en;q=0.3 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive 300
The following code sample allows you to easily get the value of the "Accept-Encoding" and "Keep-Alive" headers:
@RequestMapping("/displayHeaderInfo.do") public void displayHeaderInfo(@RequestHeader("Accept-Encoding") String encoding, @RequestHeader("Keep-Alive") long keepAlive) { //... }
This annotation is supported for annotated handler methods in Servlet and Portlet environments.
To customize request parameter binding with PropertyEditors
through Spring's WebDataBinder
, you can use
either @InitBinder
-annotated methods
within your controller or externalize your configuration by providing
a custom WebBindingInitializer
.
Annotating controller methods with
@InitBinder
allows you to configure
web data binding directly within your controller class.
@InitBinder
identifies methods that
initialize the WebDataBinder
, which will be
used to populate command and form object arguments of annotated
handler methods.
Such init-binder methods support all arguments that
@RequestMapping
supports, except for
command/form objects and corresponding validation result objects.
Init-binder methods must not have a return value. Thus, they are
usually declared as void
. Typical arguments
include WebDataBinder
in combination with
WebRequest
or
java.util.Locale
, allowing code to register
context-specific editors.
The following example demonstrates the use of
@InitBinder
to configure a
CustomDateEditor
for all
java.util.Date
form properties.
@Controller public class MyFormController { @InitBinder public void initBinder(WebDataBinder binder) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); dateFormat.setLenient(false); binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); } // ... }
To externalize data binding initialization, you can provide a
custom implementation of the
WebBindingInitializer
interface,
which you then enable by supplying a custom bean configuration for
an AnnotationMethodHandlerAdapter
, thus
overriding the default configuration.
The following example from the PetClinic application shows a
configuration using a custom implementation of the
WebBindingInitializer
interface,
org.springframework.samples.petclinic.web.ClinicBindingInitializer
,
which configures PropertyEditors required by several of the
PetClinic controllers.
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="cacheSeconds" value="0" /> <property name="webBindingInitializer"> <bean class="org.springframework.samples.petclinic.web.ClinicBindingInitializer" /> </property> </bean>