13.1. Introduction
Spring's Web MVC framework is designed around a DispatcherServlet that dispatches requests to handlers, with configurable handler mappings, view resolution, locale and theme resolution as well as support for upload files. The default handler is a very simple Controller interface, just offering a ModelAndView handleRequest(request,response) method. This can already be used for application controllers, but you will prefer the included implementation hierarchy, consisting of, for example AbstractController, AbstractCommandController and SimpleFormController. Application controllers will typically be subclasses of those. Note that you can choose an appropriate base class: if you don't have a form, you don't need a form controller. This is a major difference to Struts.
Spring Web MVC allows you to use any object as a command or form object - there is no need to implement a framework-specific interface or base class. Spring's data binding is highly flexible: for example, it treats type mismatches as validation errors that can be evaluated by the application, not as system errors. All this means that you don't need to duplicate your business objects' properties as simple, untyped strings in your form objects just to be able to handle invalid submissions, or to convert the Strings properly. Instead, it is often preferable to bind directly to your business objects. This is another major difference to Struts which is built around required base classes such as Action and ActionForm.
Compared to WebWork, Spring has more differentiated object roles. It supports the notion of a Controller, an optional command or form object, and a model that gets passed to the view. The model will normally include the command or form object but also arbitrary reference data; instead, a WebWork Action combines all those roles into one single object. WebWork does allow you to use existing business objects as part of your form, but only by making them bean properties of the respective Action class. Finally, the same Action instance that handles the request is used for evaluation and form population in the view. Thus, reference data needs to be modeled as bean properties of the Action too. These are (arguably) too many roles for one object.
Spring's view resolution is extremely flexible. A Controller implementation can even write a view directly to the response (by returning null for the ModelAndView). In the normal case, a ModelAndView instance consists of a view name and a model Map, which contains bean names and corresponding objects (like a command or form, containing reference data). View name resolution is highly configurable, either via bean names, via a properties file, or via your own ViewResolver implementation. The fact that the model (the M in MVC) is based on the Map interface allows for the complete abstraction of the view technology. Any renderer can be integrated directly, whether JSP, Velocity, or any other rendering technology. The model Map is simply transformed into an appropriate format, such as JSP request attributes or a Velocity template model.
13.1.1. Pluggability of other MVC implementations
There are several reasons why some projects will prefer to use other MVC implementations. Many teams expect to leverage their existing investment in skills and tools. In addition, there is a large body of knowledge and experience avalailable for the Struts framework. Thus, if you can live with Struts' architectural flaws, it can still be a viable choice for the web layer; the same applies to WebWork and other web MVC frameworks.
If you don't want to use Spring's web MVC, but intend to leverage other solutions that Spring offers, you can integrate the web MVC framework of your choice with Spring easily. Simply start up a Spring root application context via its ContextLoaderListener, and access it via its ServletContext attribute (or Spring's respective helper method) from within a Struts or WebWork action. Note that there aren't any "plugins" involved, so no dedicated integration is necessary. From the web layer's point of view, you'll simply use Spring as a library, with the root application context instance as the entry point.
All your registered beans and all of Spring's services can be at your fingertips even without Spring's web MVC. Spring doesn't compete with Struts or WebWork in this scenario, it just addresses the many areas that the pure web MVC frameworks don't, from bean configuration to data access and transaction handling. So you are able to enrich your application with a Spring middle tier and/or data access tier, even if you just want to use, for example, the transaction abstraction with JDBC or Hibernate.
13.1.2. Features of Spring Web MVC
Spring's web module provides a wealth of unique web support features, including:
-
Clear separation of roles - controller, validator, command object, form object, model object, DispatcherServlet, handler mapping, view resolver, etc. Each role can be fulfilled by a specialized object.
-
Powerful and straightforward configuration of both framework and application classes as JavaBeans, including easy referencing across contexts, such as from web controllers to business objects and validators.
-
Adaptability, non-intrusiveness. Use whatever controller subclass you need (plain, command, form, wizard, multi-action, or a custom one) for a given scenario instead of deriving from a single controller for everything.
-
Reusable business code - no need for duplication. You can use existing business objects as command or form objects instead of mirroring them in order to extend a particular framework base class.
-
Customizable binding and validation - type mismatches as application-level validation errors that keep the offending value, localized date and number binding, etc instead of String-only form objects with manual parsing and conversion to business objects.
-
Customizable handler mapping and view resolution - handler mapping and view resolution strategies range from simple URL-based configuration, to sophisticated, purpose-built resolution strategies. This is more flexible than some web MVC frameworks which mandate a particular technique.
-
Flexible model transfer - model transfer via a name/value Map supports easy integration with any view technology.
-
Customizable locale and theme resolution, support for JSPs with or without Spring tag library, support for JSTL, support for Velocity without the need for extra bridges, etc.
-
A simple yet powerful JSP tag library known as the Spring tag library that provides support for features such as data binding and themes. The custom tags allow for maximum flexibility in terms of markup code. For information on the tag library descriptor, see the appendix entitled Appendix D, spring.tld
-
A JSP form tag library, introduced in Spring 2.0, that makes writing forms in JSP pages much easier. For information on the tag library descriptor, see the appendix entitled Appendix E, spring-form.tld
-
Beans whose lifecycle is scoped to the current HTTP request or HTTP Session. This is not a specific feature of Spring MVC itself, but rather of the WebApplicationContext container(s) that Spring MVC uses. These bean scopes are described in detail in the section entitled Section 3.4.3, “The other scopes”
13.2. The DispatcherServlet
Spring's web MVC framework is, like many other web MVC frameworks, request-driven, designed around a central servlet that dispatches requests to controllers and offers other functionality facilitating the development of web applications. Spring's DispatcherServlet however, does more than just that. It is completely integrated with the Spring IoC container and as such allows you to use every other feature that Spring has.
The request processing workflow of the Spring Web MVC DispatcherServlet is illustrated in the following diagram. The pattern-savvy reader will recognize that the DispatcherServlet is an expression of the “Front Controller” design pattern (this is a pattern that Spring Web MVC shares with many other leading web frameworks).
?
?
The DispatcherServlet is an actual Servlet (it inherits from the HttpServlet base class), and as such is declared in the web.xml of your web application. Requests that you want the DispatcherServlet to handle will have to be mapped using a URL mapping in the same web.xml file. This is standard J2EE servlet configuration; an example of such a DispatcherServlet declaration and mapping can be found below.
<web-app> <servlet> <servlet-name>example</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>example</servlet-name> <url-pattern>*.form</url-pattern> </servlet-mapping> </web-app>
In the example above, all requests ending with .form will be handled by the 'example' DispatcherServlet. This is only the first step in setting up Spring Web MVC... the various beans used by the Spring Web MVC framework (over and above the DispatcherServlet itself) now need to be configured.
As detailed in the section entitled Section 3.8, “The ApplicationContext”, ApplicationContext instances in Spring can be scoped. In the web MVC framework, each DispatcherServlet has its own WebApplicationContext, which inherits all the beans already defined in the root WebApplicationContext. These inherited beans defined can be overridden in the servlet-specific scope, and new scope-specific beans can be defined local to a given servlet instance.
?
?
The framework will, on initialization of a DispatcherServlet, look for a file named [servlet-name]-servlet.xml in the WEB-INF directory of your web application and create the beans defined there (overriding the definitions of any beans defined with the same name in the global scope).
Consider the following DispatcherServlet servlet configuration (in the 'web.xml' file.)
<web-app> ... <servlet> <servlet-name>golfing</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>golfing</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>
With the above servlet configuration in place, you will need to have a file called '/WEB-INF/golfing-servlet.xml' in your application; this file will contain all of your Spring Web MVC-specific components (beans). The exact location of this configuration file can be changed via a servlet initialization parameter (see below for details).
The WebApplicationContext is an extension of the plain ApplicationContext that has some extra features necessary for web applications. It differs from a normal ApplicationContext in that it is capable of resolving themes (see Section 13.7, “Using themes”), and that it knows which servlet it is associated with (by having a link to the ServletContext). The WebApplicationContext is bound in the ServletContext, and by using static methods on the RequestContextUtils class you can always lookup the WebApplicationContext in case you need access to it.
The Spring DispatcherServlet has a couple of special beans it uses in order to be able to process requests and render the appropriate views. These beans are included in the Spring framework and can be configured in the WebApplicationContext, just as any other bean would be configured. Each of those beans is described in more detail below. Right now, we'll just mention them, just to let you know they exist and to enable us to go on talking about the DispatcherServlet. For most of the beans, sensible defaults are provided so you don't (initially) have to worry about configuring them.
Table 13.1. Special beans in the WebApplicationContext
Controllers | Controllers are the components that form the 'C' part of the MVC. |
Handler mappings | Handler mappings handle the execution of a list of pre- and post-processors and controllers that will be executed if they match certain criteria (for instance a matching URL specified with the controller) |
View resolvers | View resolvers are components capable of resolving view names to views |
Locale resolver | A locale resolver is a component capable of resolving the locale a client is using, in order to be able to offer internationalized views |
Theme resolver | A theme resolver is capable of resolving themes your web application can use, for example, to offer personalized layouts |
multipart file resolver | A multipart file resolver offers the functionality to process file uploads from HTML forms |
Handler exception resolver(s) | Handler exception resolvers offer functionality to map exceptions to views or implement other more complex exception handling code |
When a DispatcherServlet is set up for use and a request comes in for that specific DispatcherServlet, said DispatcherServlet starts processing the request. The list below describes the complete process a request goes through when handled by a DispatcherServlet:
-
The WebApplicationContext is searched for and bound in the request as an attribute in order for the controller and other elements in the process to use. It is bound by default under the key DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE.
-
The locale resolver is bound to the request to let elements in the process resolve the locale to use when processing the request (rendering the view, preparing data, etc.) If you don't use the resolver, it won't affect anything, so if you don't need locale resolving, you don't have to use it.
-
The theme resolver is bound to the request to let elements such as views determine which theme to use. The theme resolver does not affect anything if you don't use it, so if you don't need themes you can just ignore it.
-
If a multipart resolver is specified, the request is inspected for multiparts; if multiparts are found, the request is wrapped in a MultipartHttpServletRequest for further processing by other elements in the process. (See the section entitled Section 13.8.2, “Using the MultipartResolver” for further information about multipart handling).
-
An appropriate handler is searched for. If a handler is found, the execution chain associated with the handler (preprocessors, postprocessors, and controllers) will be executed in order to prepare a model (for rendering).
-
If a model is returned, the view is rendered. If no model is returned (which could be due to a pre- or postprocessor intercepting the request, for example, for security reasons), no view is rendered, since the request could already have been fulfilled.
Exceptions that are thrown during processing of the request get picked up by any of the handler exception resolvers that are declared in the WebApplicationContext. Using these exception resolvers allows you to define custom behaviors in case such exceptions get thrown.
The Spring DispatcherServlet also has support for returning the last-modification-date, as specified by the Servlet API. The process of determining the last modification date for a specific request is straightforward: the DispatcherServlet will first lookup an appropriate handler mapping and test if the handler that is found implements the interface LastModified interface. If so, the value of the long getLastModified(request) method of the LastModified interface is returned to the client.
You can customize Spring's DispatcherServlet by adding context parameters in the web.xml file or servlet initialization parameters. The possibilities are listed below.
Table 13.2. DispatcherServlet initialization parameters
contextClass | Class that implements WebApplicationContext, which will be used to instantiate the context used by this servlet. If this parameter isn't specified, the XmlWebApplicationContext will be used. |
contextConfigLocation | String which is passed to the context instance (specified by contextClass) to indicate where context(s) can be found. The string is potentially split up into multiple strings (using a comma as a delimiter) to support multiple contexts (in case of multiple context locations, of beans that are defined twice, the latest takes precedence). |
namespace | the namespace of the WebApplicationContext. Defaults to [server-name]-servlet. |
13.3. Controllers
The notion of a controller is part of the MVC design pattern (more specifically it is the 'C' in MVC. Controllers provide access to the application behavior which is typically defined by a service interface. Controllers interpret user input and transform said input into a sensible model which will be represented to the user by the view. Spring has implemented the notion of a controller in a very abstract way enabling a wide variety of different kinds of controllers to be created. Spring contains form-specific controllers, command-based controllers, and controllers that execute wizard-style logic, to name but a few.
Spring's basis for the controller architecture is the org.springframework.web.servlet.mvc.Controller interface, the source code for which is listed below.
public interface Controller { /** * Process the request and return a ModelAndView object which the DispatcherServlet * will render. */ ModelAndView handleRequest( HttpServletRequest request, HttpServletResponse response) throws Exception; }
As you can see, the Controller interface defines a single method that is responsible for handling a request and returning an appropriate model and view. These three concepts are the basis for the Spring MVC implementation - ModelAndView and Controller. While the Controller interface is quite abstract, Spring offers a lot of Controller implementations out of the box that already contain a lot of the functionality you might need. The Controller interface just defines the most basic responsibility required of every controller; namely handling a request and returning a model and a view.
13.3.1. AbstractController and WebContentGenerator
To provide a basic infrastructure, all of Spring's various Controller inherit from AbstractController, a class offering caching support and, for example, the setting of the mimetype.
Table 13.3. Features offered by the AbstractController
supportedMethods | indicates what methods this controller should accept. Usually this is set to both GET and POST, but you can modify this to reflect the method you want to support. If a request is received with a method that is not supported by the controller, the client will be informed of this (expedited by the throwing of a ServletException). |
requiresSession | indicates whether or not this controller requires a HTTP session to do its work. If a session is not present when such a controller receives a request, the user is informed of this by a ServletException being thrown. |
synchronizeSession | use this if you want handling by this controller to be synchronized on the user's HTTP session. |
cacheSeconds | when you want a controller to generate a caching directive in the HTTP response, specify a positive integer here. By default the value of this property is set to -1 so no caching directives will be included in the generated response. |
useExpiresHeader | tweaks your controllers to specify the HTTP 1.0 compatible "Expires" header in the generated response. By default the value of this property is true. |
useCacheHeader | tweaks your controllers to specify the HTTP 1.1 compatible "Cache-Control" header in the generated response. By default the value of this property is true. |
When using the AbstractController as the baseclass for your controllers you only have to override the handleRequestInternal(HttpServletRequest, HttpServletResponse) method, implement your logic, and return a ModelAndView object. Here is short example consisting of a class and a declaration in the web application context.
package samples; public class SampleController extends AbstractController { public ModelAndView handleRequestInternal( HttpServletRequest request, HttpServletResponse response) throws Exception { ModelAndView mav = new ModelAndView("hello"); mav.addObject("message", "Hello World!"); return mav; } }
<bean id="sampleController" class="samples.SampleController"> <property name="cacheSeconds" value="120"/> </bean>
The above class and the declaration in the web application context is all you need besides setting up a handler mapping (see the section entitled Section 13.4, “Handler mappings”) to get this very simple controller working. This controller will generate caching directives telling the client to cache things for 2 minutes before rechecking. This controller also returns a hard-coded view (which is typically considered bad practice).
13.3.2. Other simple controllers
Although you can extend AbstractController, Spring provides a number of concrete implementations which offer functionality that is commonly used in simple MVC applications. The ParameterizableViewController is basically the same as the example above, except for the fact that you can specify the view name that it will return in the web application context (and thus remove the need to hard-code the viewname in the Java class).
The UrlFilenameViewController inspects the URL and retrieves the filename of the file request and uses that as a viewname. For example, the filename of http://www.springframework.org/index.html request is index.
13.3.3. The MultiActionController
Spring offers a multi-action controller with which you aggregate multiple actions into one controller, thus grouping functionality together. The multi-action controller lives in a separate package - org.springframework.web.servlet.mvc.multiaction - and is capable of mapping requests to method names and then invoking the right method name. Using the multi-action controller is especially handy when you have a lot of common functionality in one controller, but want to have multiple entry points to the controller, for example, to tweak behavior.
Table 13.4. Features offered by the MultiActionController
delegate | there are two usage-scenarios for the MultiActionController. Either you subclass the MultiActionController and specify the methods that will be resolved by the MethodNameResolver on the subclass (in which case you don't need to set the delegate), or you define a delegate object, on which methods resolved by the MethodNameResolver will be invoked. If you choose this scenario, you will have to define the delegate using this configuration parameter as a collaborator. |
methodNameResolver | the MultiActionController needs a strategy to resolve the method it has to invoke, based on the incoming request. This strategy is defined by the MethodNameResolver interface; the MultiActionController exposes a property sp that you can supply a resolver that is capable of doing that. |
Methods defined for a multi-action controller need to conform to the following signature:
// anyMeaningfulName can be replaced by any methodname
public [ModelAndView | Map | void] anyMeaningfulName(HttpServletRequest, HttpServletResponse [, Exception | AnyObject]);
Please note that method overloading is not allowed since it would confuse the MultiActionController. Furthermore, you can define exception handlers capable of handling exceptions that are thrown by the methods you specify.
The (optional) Exception argument can be any exception, as long as it's a subclass of java.lang.Exception or java.lang.RuntimeException. The (optional) AnyObject argument can be any class. Request parameters will be bound onto this object for convenient consumption.
?
Find below some examples of valid MultiActionController method signatures.
?
The standard signature (mirrors the Controller interface method).
?
public ModelAndView doRequest(HttpServletRequest, HttpServletResponse)
?
This signature accepts a Login argument that will be populated (bound) with parameters stripped from the request
?
public ModelAndView doLogin(HttpServletRequest, HttpServletResponse, Login)
?
The signature for an Exception handling method.
?
public ModelAndView processException(HttpServletRequest, HttpServletResponse, IllegalArgumentException)
?
This signature has a void return type (see the section entitled Section 13.11, “Convention over configuration” below).
?
public void goHome(HttpServletRequest, HttpServletResponse)
?
This signature has a Map return type (see the section entitled Section 13.11, “Convention over configuration” below).
?
public Map doRequest(HttpServletRequest, HttpServletResponse)
?
The MethodNameResolver is responsible for resolving method names based on the request coming in. Find below details about the three MethodNameResolver implementations that Spring provides out of the box.
?
-
ParameterMethodNameResolver - capable of resolving a request parameter and using that as the method name (http://www.sf.net/index.view?testParam=testIt will result in a method testIt(HttpServletRequest, HttpServletResponse) being called). The paramName property specifies the request parameter that is to be inspected).
-
InternalPathMethodNameResolver - retrieves the filename from the request path and uses that as the method name (http://www.sf.net/testing.view will result in a method testing(HttpServletRequest, HttpServletResponse) being called).
-
PropertiesMethodNameResolver - uses a user-defined properties object with request URLs mapped to method names. When the properties contain /index/welcome.html=doIt and a request to /index/welcome.html comes in, the doIt(HttpServletRequest, HttpServletResponse) method is called. This method name resolver works with the PathMatcher, so if the properties contained /**/welcom?.html, it would also have worked!
?
Here are a couple of examples. First, an example showing the ParameterMethodNameResolver and the delegate property, which will accept requests to URLs with the parameter method included and set to retrieveIndex:
?
<bean id="paramResolver" class="org....mvc.multiaction.ParameterMethodNameResolver">
<property name="paramName" value="method"/>
</bean>
<bean id="paramMultiController" class="org....mvc.multiaction.MultiActionController">
<property name="methodNameResolver" ref="paramResolver"/>
<property name="delegate" ref="sampleDelegate"/>
</bean>
<bean id="sampleDelegate" class="samples.SampleDelegate"/>
## together with
public class SampleDelegate {
public ModelAndView retrieveIndex(HttpServletRequest req, HttpServletResponse resp) {
return new ModelAndView("index", "date", new Long(System.currentTimeMillis()));
}
}
?
When using the delegates shown above, we could also use the PropertiesMethodNameResolver to match a couple of URLs to the method we defined:
?
<bean id="propsResolver" class="org....mvc.multiaction.PropertiesMethodNameResolver"> <property name="mappings"> <value> /index/welcome.html=retrieveIndex /**/notwelcome.html=retrieveIndex /*/user?.html=retrieveIndex </value> </property> </bean> <bean id="paramMultiController" class="org....mvc.multiaction.MultiActionController"> <property name="methodNameResolver" ref="propsResolver"/> <property name="delegate" ref="sampleDelegate"/> </bean>
?
13.3.4. Command controllers
Spring's command controllers are a fundamental part of the Spring Web MVC package. Command controllers provide a way to interact with data objects and dynamically bind parameters from the HttpServletRequest to the data object specified. They perform a somewhat similar role to the Struts ActionForm, but in Spring, your data objects don't have to implement a framework-specific interface. First, lets examine what command controllers are available straight out of the box.
-
AbstractCommandController - a command controller you can use to create your own command controller, capable of binding request parameters to a data object you specify. This class does not offer form functionality; it does however offer validation features and lets you specify in the controller itself what to do with the command object that has been populated with request parameter values.
-
AbstractFormController - an abstract controller offering form submission support. Using this controller you can model forms and populate them using a command object you retrieve in the controller. After a user has filled the form, the AbstractFormController binds the fields, validates the command object, and hands the object back to the controller to take the appropriate action. Supported features are: invalid form submission (resubmission), validation, and normal form workflow. You implement methods to determine which views are used for form presentation and success. Use this controller if you need forms, but don't want to specify what views you're going to show the user in the application context.
-
SimpleFormController - a form controller that provides even more support when creating a form with a corresponding command object. The SimpleFormController let's you specify a command object, a viewname for the form, a viewname for page you want to show the user when form submission has succeeded, and more.
-
AbstractWizardFormController - as the class name suggests, this is an abstract class - your wizard controller should extend it. This means you have to implement the validatePage(), processFinish() and processCancel() methods.
You probably also want to write a contractor, which should at the very least call setPages() and setCommandName(). The former takes as its argument an array of type String. This array is the list of views which comprise your wizard. The latter takes as its argument a String, which will be used to refer to your command object from within your views.
As with any instance of AbstractFormController, you are required to use a command object - a JavaBean which will be populated with the data from your forms. You can do this in one of two ways: either call setCommandClass() from the constructor with the class of your command object, or implement the formBackingObject() method.
AbstractWizardFormController has a number of concrete methods that you may wish to override. Of these, the ones you are likely to find most useful are: referenceData(..) which you can use to pass model data to your view in the form of a Map; getTargetPage() if your wizard needs to change page order or omit pages dynamically; and onBindAndValidate() if you want to override the built-in binding and validation workflow.
Finally, it is worth pointing out the setAllowDirtyBack() and setAllowDirtyForward(), which you can call from getTargetPage() to allow users to move backwards and forwards in the wizard even if validation fails for the current page.
For a full list of methods, see the Javadoc for AbstractWizardFormController. There is an implemented example of this wizard in the jPetStore included in the Spring distribution: org.springframework.samples.jpetstore.web.spring.OrderFormController.
13.4. Handler mappings
Using a handler mapping you can map incoming web requests to appropriate handlers. There are some handler mappings you can use out of the box, for example, the SimpleUrlHandlerMapping or the BeanNameUrlHandlerMapping, but let's first examine the general concept of a HandlerMapping.
The functionality a basic HandlerMapping provides is the delivering of a HandlerExecutionChain, which must contain the handler that matches the incoming request, and may also contain a list of handler interceptors that are applied to the request. When a request comes in, the DispatcherServlet will hand it over to the handler mapping to let it inspect the request and come up with an appropriate HandlerExecutionChain. Then the DispatcherServlet will execute the handler and interceptors in the chain (if any).
The concept of configurable handler mappings that can optionally contain interceptors (executed before or after the actual handler was executed, or both) is extremely powerful. A lot of supporting functionality can be built into custom HandlerMappings. Think of a custom handler mapping that chooses a handler not only based on the URL of the request coming in, but also on a specific state of the session associated with the request.
This section describes two of Spring's most commonly used handler mappings. They both extend the AbstractHandlerMapping and share the following properties:
-
interceptors: the list of interceptors to use. HandlerInterceptors are discussed in Section 13.4.3, “Intercepting requests - the HandlerInterceptor interface”.
-
defaultHandler: the default handler to use, when this handler mapping does not result in a matching handler.
-
order: based on the value of the order property (see the org.springframework.core.Ordered interface), Spring will sort all handler mappings available in the context and apply the first matching handler.
-
alwaysUseFullPath: if this property is set to true, Spring will use the full path within the current servlet context to find an appropriate handler. If this property is set to false (the default), the path within the current servlet mapping will be used. For example, if a servlet is mapped using /testing/* and the alwaysUseFullPath property is set to true, /testing/viewPage.html would be used, whereas if the property is set to false, /viewPage.html would be used.
-
urlPathHelper: using this property, you can tweak the UrlPathHelper used when inspecting URLs. Normally, you shouldn't have to change the default value.
-
urlDecode: the default value for this property is false. The HttpServletRequest returns request URLs and URIs that are not decoded. If you do want them to be decoded before a HandlerMapping uses them to find an appropriate handler, you have to set this to true (note that this requires JDK 1.4). The decoding method uses either the encoding specified by the request or the default ISO-8859-1 encoding scheme.
-
lazyInitHandlers: allows for lazy initialization of singleton handlers (prototype handlers are always lazily initialized). Default value is false.
(Note: the last four properties are only available to subclasses of org.springframework.web.servlet.handler.AbstractUrlHandlerMapping).
13.4.1. BeanNameUrlHandlerMapping
A very simple, but very powerful handler mapping is the BeanNameUrlHandlerMapping, which maps incoming HTTP requests to names of beans, defined in the web application context. Let's say we want to enable a user to insert an account and we've already provided an appropriate form controller (see Section 13.3.4, “Command controllers” for more information on command- and form controllers) and a JSP view (or Velocity template) that renders the form. When using the BeanNameUrlHandlerMapping, we could map the HTTP request with the URL http://samples.com/editaccount.form to the appropriate form Controller as follows:
<beans> <bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <bean name="/editaccount.form" class="org.springframework.web.servlet.mvc.SimpleFormController"> <property name="formView" value="account"/> <property name="successView" value="account-created"/> <property name="commandName" value="account"/> <property name="commandClass" value="samples.Account"/> </bean> <beans>
All incoming requests for the URL /editaccount.form will now be handled by the form Controller in the source listing above. Of course we have to define a servlet-mapping in web.xml as well, to let through all the requests ending with .form.
<web-app>
...
<servlet>
<servlet-name>sample</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- maps the sample dispatcher to *.form -->
<servlet-mapping>
<servlet-name>sample</servlet-name>
<url-pattern>*.form</url-pattern>
</servlet-mapping>
...
</web-app>
Note |
If you want to use the BeanNameUrlHandlerMapping, you don't necessarily have to define it in the web application context (as indicated above). By default, if no handler mapping can be found in the context, the DispatcherServlet creates a BeanNameUrlHandlerMapping for you! |
13.4.2. SimpleUrlHandlerMapping
A further - and much more powerful handler mapping - is the SimpleUrlHandlerMapping. This mapping is configurable in the application context and has Ant-style path matching capabilities (see the Javadoc for the org.springframework.util.PathMatcher class). Here is an example:
<web-app> ... <servlet> <servlet-name>sample</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <!-- maps the sample dispatcher to *.form --> <servlet-mapping> <servlet-name>sample</servlet-name> <url-pattern>*.form</url-pattern> </servlet-mapping> <!-- maps the sample dispatcher to *.html --> <servlet-mapping> <servlet-name>sample</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> ... </web-app>
The above web.xml configuration snippet enables all requests ending with .html and .form to be handled by the sample dispatcher servlet.
<beans>
<!-- no 'id' required, HandlerMapping beans are automatically detected by the DispatcherServlet -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/*/account.form=editAccountFormController
/*/editaccount.form=editAccountFormController
/ex/view*.html=helpController
/**/help.html=helpController
</value>
</property>
</bean>
<bean id="helpController"
class="org.springframework.web.servlet.mvc.UrlFilenameViewController"/>
<bean id="editAccountFormController"
class="org.springframework.web.servlet.mvc.SimpleFormController">
<property name="formView" value="account"/>
<property name="successView" value="account-created"/>
<property name="commandName" value="Account"/>
<property name="commandClass" value="samples.Account"/>
</bean>
<beans>
This handler mapping routes requests for 'help.html' in any directory to the 'helpController', which is a UrlFilenameViewController (more about controllers can be found in the section entitled Section 13.3, “Controllers”). Requests for a resource beginning with 'view', and ending with '.html' in the directory 'ex' will be routed to the 'helpController'. Two further mappings are also defined for 'editAccountFormController'.
13.4.3. Intercepting requests - the HandlerInterceptor interface
Spring's handler mapping mechanism has the notion of handler interceptors, that can be extremely useful when you want to apply specific functionality to certain requests, for example, checking for a principal.
Interceptors located in the handler mapping must implement HandlerInterceptor from the org.springframework.web.servlet package. This interface defines three methods, one that will be called before the actual handler will be executed, one that will be called after the handler is executed, and one that is called after the complete request has finished. These three methods should provide enough flexibility to do all kinds of pre- and post-processing.
The preHandle(..) method returns a boolean value. You can use this method to break or continue the processing of the execution chain. When this method returns true, the handler execution chain will continue, when it returns false, the DispatcherServlet assumes the interceptor itself has taken care of requests (and, for example, rendered an appropriate view) and does not continue executing the other interceptors and the actual handler in the execution chain.
The following example provides an interceptor that intercepts all requests and reroutes the user to a specific page if the time is not between 9 a.m. and 6 p.m.
<beans> <bean id="handlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="interceptors"> <list> <ref bean="officeHoursInterceptor"/> </list> </property> <property name="mappings"> <value> /*.form=editAccountFormController /*.view=editAccountFormController </value> </property> </bean> <bean id="officeHoursInterceptor" class="samples.TimeBasedAccessInterceptor"> <property name="openingTime" value="9"/> <property name="closingTime" value="18"/> </bean> <beans>
package samples; public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter { private int openingTime; private int closingTime; public void setOpeningTime(int openingTime) { this.openingTime = openingTime; } public void setClosingTime(int closingTime) { this.closingTime = closingTime; } public boolean preHandle( HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Calendar cal = Calendar.getInstance(); int hour = cal.get(HOUR_OF_DAY); if (openingTime <= hour < closingTime) { return true; } else { response.sendRedirect("http://host.com/outsideOfficeHours.html"); return false; } } }
Any request coming in, will be intercepted by the TimeBasedAccessInterceptor, and if the current time is outside office hours, the user will be redirected to a static html file, saying, for example, he can only access the website during office hours.
As you can see, Spring has an adapter class (the cunningly named HandlerInterceptorAdapter) to make it easier to extend the HandlerInterceptor interface.
13.5. Views and resolving them
All MVC frameworks for web applications provide a way to address views. Spring provides view resolvers, which enable you to render models in a browser without tying you to a specific view technology. Out of the box, Spring enables you to use JSPs, Velocity templates and XSLT views, for example. The section entitled Chapter 14, Integrating view technologies has details of how to integrate and use a number of disparate view technologies.
The two interfaces which are important to the way Spring handles views are ViewResolver and View. The ViewResolver provides a mapping between view names and actual views. The View interface addresses the preparation of the request and hands the request over to one of the view technologies.
13.5.1. Resolving views - the ViewResolver interface
As discussed in the section entitled Section 13.3, “Controllers”, all controllers in the Spring Web MVC framework return a ModelAndView instance. Views in Spring are addressed by a view name and are resolved by a view resolver. Spring comes with quite a few view resolvers. We'll list most of them and then provide a couple of examples.
Table 13.5. View resolvers