For a lot of projects, sticking to established conventions and
having reasonable defaults is just what they (the projects) need... this
theme of convention-over-configuration now has explicit support in Spring
Web MVC. What this means is that if you establish a set of naming
conventions and suchlike, you can substantially cut
down on the amount of configuration that is required to set up handler
mappings, view resolvers, ModelAndView instances,
etc. This is a great boon with regards to rapid prototyping, and can also
lend a degree of (always good-to-have) consistency across a codebase
should you choose to move forward with it into production.
Convention-over-configuration support addresses the three core areas of MVC -- models, views, and controllers.
The ControllerClassNameHandlerMapping class
is a HandlerMapping implementation that
uses a convention to determine the mapping between request URLs and the
Controller instances that are to handle
those requests.
Consider the following simple
Controller implementation. Take special
notice of the name of the class.
public class ViewShoppingCartController implements Controller { public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) { // the implementation is not hugely important for this example... } }
Here is a snippet from the attendent Spring Web MVC configuration file...
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/> <bean id="viewShoppingCart" class="x.y.z.ViewShoppingCartController"> <!-- inject dependencies as required... --> </bean>
The ControllerClassNameHandlerMapping finds
all of the various handler (or
Controller) beans defined in its
application context and strips Controller off the
name to define its handler mappings. Thus,
ViewShoppingCartController maps to the
/viewshoppingcart* request URL.
Let's look at some more examples so that the central idea becomes
immediately familiar. (Notice all lowercase in the URLs, in contrast to
camel-cased Controller class
names.)
WelcomeController maps to the
/welcome* request URL
HomeController maps to the
/home* request URL
IndexController maps to the
/index* request URL
RegisterController maps to the
/register* request URL
In the case of MultiActionController
handler classes, the mappings generated are slightly more complex. The
Controller names in the following
examples are assumed to be MultiActionController
implementations:
AdminController maps to the
/admin/* request
URL
CatalogController maps to the
/catalog/*
request URL
If you follow the convention of naming your
Controller implementations as
xxxController, the
ControllerClassNameHandlerMapping saves you the
tedium of defining and maintaining a potentially
looooong
SimpleUrlHandlerMapping (or suchlike).
The ControllerClassNameHandlerMapping class
extends the AbstractHandlerMapping base class so
you can define HandlerInterceptor
instances and everything else just as you would with many other
HandlerMapping implementations.
The ModelMap class is essentially a
glorified Map that can make adding
objects that are to be displayed in (or on) a
View adhere to a common naming
convention. Consider the following
Controller implementation; notice that
objects are added to the ModelAndView without any
associated name specified.
public class DisplayShoppingCartController implements Controller { public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) { List cartItems = // get a List of CartItem objects User user = // get the User doing the shopping ModelAndView mav = new ModelAndView("displayShoppingCart"); <-- the logical view name mav.addObject(cartItems); <-- look ma, no name, just the object mav.addObject(user); <-- and again ma! return mav; } }
The ModelAndView class uses a
ModelMap class that is a custom
Map implementation that automatically
generates a key for an object when an object is added to it. The
strategy for determining the name for an added object is, in the case of
a scalar object such as User, to use the short
class name of the object's class. The following examples are names that
are generated for scalar objects put into a
ModelMap instance.
An x.y.User instance added will have
the name user generated.
An x.y.Registration instance added will
have the name registration generated.
An x.y.Foo instance added will have the
name foo generated.
A java.util.HashMap instance added will
have the name hashMap generated. You probably
want to be explicit about the name in this case because
hashMap is less than intuitive.
Adding null will result in an
IllegalArgumentException being thrown. If the
object (or objects) that you are adding could be
null, then you will also want to be explicit
about the name.
The strategy for generating a name after adding a
Set, List
or array object is to peek into the collection, take the short class
name of the first object in the collection, and use that with
List appended to the name. Some examples will make
the semantics of name generation for collections clearer...
An x.y.User[] array with one or more
x.y.User elements added will have the name
userList generated.
An x.y.Foo[] array with one or more
x.y.User elements added will have the name
fooList generated.
A java.util.ArrayList with one or more
x.y.User elements added will have the name
userList generated.
A java.util.HashSet with one or more
x.y.Foo elements added will have the name
fooList generated.
An empty
java.util.ArrayList will not be added at all
(in effect, the addObject(..) call will
essentially be a no-op).
The RequestToViewNameTranslator
interface determines a logical View name
when no such logical view name is explicitly supplied. It has just one
implementation, the
DefaultRequestToViewNameTranslator class.
The DefaultRequestToViewNameTranslator maps
request URLs to logical view names, as with this example:
public class RegistrationController implements Controller { public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) { // process the request... ModelAndView mav = new ModelAndView(); // add data as necessary to the model... return mav; // notice that no View or logical view name has been set } }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"> <beans> <!-- this bean with the well known name generates view names for us --> <bean id="viewNameTranslator" class="org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator"/> <bean class="x.y.RegistrationController"> <!-- inject dependencies as necessary --> </bean> <!-- maps request URLs to Controller names --> <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> </beans>
Notice how in the implementation of the
handleRequest(..) method no
View or logical view name is ever set on
the ModelAndView that is returned. The
DefaultRequestToViewNameTranslator is tasked with
generating a logical view name from the URL of the
request. In the case of the above
RegistrationController, which is used in
conjunction with the
ControllerClassNameHandlerMapping, a request URL
of http://localhost/registration.html results in a
logical view name of registration being generated by
the DefaultRequestToViewNameTranslator. This
logical view name is then resolved into the
/WEB-INF/jsp/registration.jsp view by the
InternalResourceViewResolver bean.
![]() | Tip |
|---|---|
You do not need to define a
|
Of course, if you need to change the default settings, then you do
need to configure your own
DefaultRequestToViewNameTranslator bean
explicitly. Consult the comprehensive Javadoc for the
DefaultRequestToViewNameTranslator class for
details of the various properties that can be configured.