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
HandlerMapping
s. 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. HandlerInterceptor
s are discussed
in Section 16.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.
urlDecode
: the default value for this
property is true
, as of Spring 2.5. If you prefer
to compare encoded paths, switch this flag to
false
. However, note that the
HttpServletRequest
always exposes the
servlet path in decoded form. Be aware that the servlet path will not
match when compared with encoded paths.
lazyInitHandlers
: allows for lazy
initialization of singleton handlers (prototype
handlers are always lazily initialized). Default value is
false
.
(Note: the last three properties are only available to
subclasses of
org.springframework.web.servlet.handler.AbstractUrlHandlerMapping
).
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 16.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
|
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 16.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'
.
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.