If DispatcherServlet is the heart of Spring MVC then controllers are the brains.
When implementing the behavior of your Spring MVC application, you extend
one of Spring’s controller classes. The controller receives requests from DispatcherServlet
and performs some business functionality on behalf of the user.
If you’re familiar with other web frameworks such as Struts or WebWork, you
may recognize controllers as being roughly equivalent in purpose to a Struts or
WebWork action. One huge difference between Spring controllers and Struts/WebWork actions, however, is that Spring provides a rich controller hierarchy (as
shown in Figure 13.6) in contrast to the rather flat action hierarchy of Struts or
WebWork.
At first glance, Figure 13.6 may seem somewhat daunting. Indeed, when compared
to other MVC frameworks such as Jakarta Struts or WebWork, there’s a lot
more to swallow with Spring’s controller hierarchy. In reality, however, this perceived
complexity is actually quite simple and flexible.
At the top of the controller hierarchy is the Controller interface. Any class
implementing this interface can be used to handle requests through the Spring
MVC framework. To create your own controller, all you must do is write a class that
implements this interface.
While you could write a class that directly implements the Controller interface,
you’re more likely to extend one of the classes lower in the hierarchy.
Whereas the Controller interface defines the basic contract between a controller
and Spring MVC, the various controller classes provide additional functionality
beyond the basics.
The wide selection of controller classes is both a blessing and a curse.
Unlike other frameworks that force you to work with a single type of controller
object (such as Struts’s Action class), Spring lets you choose the controller that
is most appropriate for your needs. However, with so many controller classes to
choose from, many developers find themselves overwhelmed and don’t know
how to decide.
To help you decide which controller class to extend for your application’s
controllers, consider table 13.2. As you can see, Spring’s controller classes can be
grouped into six categories that provide more functionality (and introduce
more complexity) as you progress down the table. You may also notice from figure
13.5 that (with the exception of ThrowawayController) as you move down
the controller hierarchy, each controller builds on the functionality of the controllers
above it.
You’ve already seen an example of a simple controller that extends Abstract-Controller. In Listing 13.1, HomePageController extends AbstractController
and retrieves a list of the most recent rants for display on the home page.
AbstractController is a perfect choice because the homepage is so simple and
takes no input.
Basing your controller on AbstractController is fine when you don’t need a
lot of power. Most controllers, however, are going to be more interesting, taking
parameters and requiring validation of those parameters. In the sections that follow,
we’re going to build several controllers that define the web layer of the
RoadRantz application by extending the other implementations of the Controller
classes in Figure 13.6, starting with command controllers.
13.3.1 Processing commands
It’s common for a web request to take one or more parameters that are used to
determine the results. For instance, one of the requirements for the RoadRantz
application is to display a list of rants for a particular vehicle.
Of course, you could extend AbstractController and retrieve the parameters
your controller needs from the HttpServletRequest. But you would also have to
write the logic that binds the parameters to business objects and you’d have to put
validation logic in the controller itself. Binding and validation logic really don’t
belong in the controller.
In the event that your controller will need to perform work based on parameters,
your controller class should extend a command controller class such as
AbstractCommandController. As shown in Figure 13.7, command controllers
automatically bind request parameters to a command object. They can also be
wired to plug in validators to ensure that the parameters are valid.
Listing 13.3 shows RantsForVehicleController, a command controller that is
used to display a list of rants that have been entered for a specific vehicle.
The handle() method of RantsForVehicleController is the main execution
method for AbstractCommandController. This method is a bit more interesting
than the handleRequestInternal() method from AbstractController. In addition
to an HttpServletRequest and an HttpServletResponse, handle() takes an
Object that is the controller’s command.
A command object is a bean that is meant to hold request parameters for easy
access. If you are familiar with Jakarta Struts, you may recognize a command
object as being similar to a Struts ActionForm. The key difference is that unlike a
Struts form bean that must extend ActionForm, a Spring command object is a POJO
that doesn’t need to extend any Spring-specific classes.
In this case, the command object is an instance of Vehicle, as set in the controller’s
constructor. You may recognize Vehicle as the domain class that
describes a vehicle from chapter 5. Although command classes don’t have to be
instances of domain classes, it is sure handy when they are. Vehicle already
defines the same data needed by RantsForVehicleController. Conveniently, it’s
also the exact same type needed by the getRantsForVehicle() method of RantService. This makes it a perfect choice for a command class.
Before the handle() method is called, Spring will attempt to match any
parameters passed in the request to properties in the command object. Vehicle
has two properties: state and plateNumber. If the request has parameters with
these names, the parameter values will automatically be bound to the Vehicle’s
properties.
As with HomePageController, you’ll also need to register RantsForVehicleController in roadrantz-servlet.xml:
Command controllers make it easy to handle requests with request parameters by
binding the request parameters to command objects. The request parameters
could be given as URL parameters (as is likely the case with RantsForVehicleController)
or as fields from a web-based form. Although command controllers can
process input from a form, Spring provides another type of controller with better
support for form handling. Let’s have a look at Spring’s form controllers next.
13.3.2 Processing form submissions
In a typical web-based application, you’re likely to encounter at least one form
that you must fill out. When you submit that form, the data that you enter is sent
to the server for processing, and once the processing is completed, you are either
presented with a success page or are given the form page with errors in your submission
that you must correct.
The core functionality of the RoadRantz application is the ability to enter a
rant about a particular vehicle. In the application, the user will be presented with
a form to enter their rant. Upon submission of that form, the expectation is that
the rant will be saved to the database for later viewing.
When implementing the rant submission process, you might be tempted to
extend AbstractController to display the form and to extend AbstractCommandController to process the form. This could certainly work, but would end up
being more difficult than necessary. You would have to maintain two different
controllers that work in tandem to process rant submissions. Wouldn’t it be simpler
to have a single controller handle both form display and form processing?
What you’ll need in this case is a form controller. Form controllers take the
concept of command controllers a step further, as shown in Figure 13.8, by adding
functionality to display a form when an HTTP GET request is received and process
the form when an HTTP POST is received. Furthermore, if any errors occur in processing
the form, the controller will know to redisplay the form so that the user
can correct the errors and resubmit.
To illustrate how form controllers work, consider AddRantFormController in
Listing 13.4.
Although it may not be obvious, AddRantFormController is responsible for both
displaying a rant entry form and processing the results of that form. When this
controller receives an HTTP GET request, it will direct the request to the form
view. And when it receives an HTTP POST request, the onSubmit() method will
process the form submission.
The referenceData() method is optional, but is handy when you need to provide
any additional information for displaying the form. In this case, our form will
need a list of states that will be displayed (presumably in a drop-down selection
list). So, the referenceData() method of AddRantFormController adds an array
of Strings that contains all 50 U.S. states as well as the District of Columbia.
Under normal circumstances, the command object that backs the form is simply
an instance of the command class. In the case of AddRantFormController,
however, a simple Rant instance will not do. The form is going to use the nested
Vehicle property within a Rant as part of the form-backing object. Therefore, it
was necessary to override the formBackingObject() method to set the vehicle
property. Otherwise, a NullPointerException would be thrown when the controller
attempts to bind the state and plateNumber properties.
The onSubmit() method handles the form submission (an HTTP POST
request) by passing the command object (which is an instance of Rant) to the
addRant() method of the injected RantService reference.
What’s not clear from Listing 13.4 is how this controller knows to display the
rant entry form. It’s also not clear where the user will be taken after the rant has
been successfully added. The only hint is that the result of a call to getSuccessView() is given to the ModelAndView. But where does the success view come from?
SimpleFormController is designed to keep view details out of the controller’s
Java code as much as possible. Instead of hard-coding a ModelAndView object, you
configure the form controller in the context configuration file as follows:
Just as with the other controllers, the addRantController bean is wired with any
services that it may need (e.g., rantService). But here you also specify a
formView property and a successView property. The formView property is the
logical name of a view to display when the controller receives an HTTP GET
request or when any errors are encountered. Likewise, the successView is the logical
name of a view to display when the form has been submitted successfully. A
view resolver (see section 13.4) will use these values to locate the View object that
will render the output to the user.
Validating form input
When AddRantFormController calls addRant(), it’s important to ensure that all
of the data in the Rant command is valid and complete. You don’t want to let
users enter only a state and no plate number (or vice versa). Likewise, what’s the
point in specifying a state and plate number but not providing any text in the
rant? And it’s important that the user not enter a plate number that isn’t valid.
The org.springframework.validation.Validator interface accommodates
validation for Spring MVC. It is defined as follows:
Implementations of this interface should examine the fields of the object passed
into the validate() method and reject any invalid values via the Errors object.
The supports() method is used to help Spring determine whether the validator
can be used for a given class.
RantValidator (Listing 13.5) is a Validator implementation used to validate a
Rant object.
The only other thing to do is to configure AddRantFormController to use
RantValidator. You can do this by wiring a RantValidator bean into the
AddRantFormController bean (shown here as an inner bean):
When a rant is entered, if all of the required properties are set and if the plate
number passes validation, AddRantFormController’s onSubmit() will be called
and the rant will be added. However, if RantValidator rejects any of the fields,
the user will be returned to the form view to correct the mistakes.
By implementing the Validator interface, you are able to programmatically
take full control over the validation of your application’s command objects. This
may be perfect if your validation needs are complex and require special logic.
However, in simple cases such as ensuring required fields and basic formatting,
writing our own implementation of the Validator interface is a bit too involved.
It’d be nice if we could write validation rules declaratively instead of having to
write validation rules in Java code. Let’s have a look at how to use declarative validation
with Spring MVC.
Validating with Commons Validator
One complaint that we’ve heard about Spring MVC is that validation with the Validator
interface doesn’t even compare to the kind of validation possible with
Jakarta Struts. We can’t argue with that complaint. Jakarta Struts has a very nice
facility for declaring validation rules outside of Java code. The good news is that
we can do declarative validation with Spring MVC, too.
But before you go digging around in Spring’s JavaDoc for a declarative Validator
implementation, you should know that Spring doesn’t come with such a validator.
In fact, Spring doesn’t come with any implementations of the Validator
interface and leaves it up to you to write your own.
However, you don’t have to go very far to find an implementation of Validator
that supports declarative validation. The Spring Modules project (https://
springmodules.dev.java.net) is a sister project of Spring that provides several
extensions to Spring whose scope exceeds that of the main Spring project. One
of those extensions is a validation module that makes use of Jakarta Commons
Validator (http://jakarta.apache.org/commons/validator) to provide declarative
validation.
To use the validation module in your application, you start by making the
springmodules-validator.jar file available in the application’s classpath. If you’re
using Ant to do your builds, you’ll need to download the Spring Modules distribution
(I’m using version 0.6) and find the spring-modules-0.6.jar file in the dist
directory. Add this JAR to the <war> task’s <lib> to ensure that it gets placed in
the WEB-INF/lib directory of the application’s WAR file.
If you’re using Maven 2 to do your build (as I’m doing), you’ll need to add the
following <dependency> to pom.xml:
Spring Modules provides an implementation of Validator called DefaultBeanValidator. DefaultBeanValidator is configured in roadrantz-servlet.xml as follows:
DefaultBeanValidator doesn’t do any actual validation work. Instead, it delegates
to Commons Validator to validate field values. As you can see, DefaultBeanValidator has a validatorFactory property that is wired with a reference to a
validatorFactory bean. The validatorFactory bean is declared using the following
XML:
DefaultValidatorFactory is a class that loads the Commons Validator configuration
on behalf of DefaultBeanValidator. The validationConfigLocations
property takes a list of one or more validation configurations. Here we’ve asked it
to load two configurations: validator-rules.xml and validation.xml.
The validator-rules.xml file contains a set of predefined validation rules for
common validation needs such as email and credit card numbers. This file comes
with the Commons Validator distribution, so you won’t have to write it yourself—simply add it to the WEB-INF directory of your application. Table 13.3 lists all of
the validation rules that come in validator-rules.xml.
The other file, validation.xml, defines application-specific validation rules that
apply directly to the RoadRantz application. Listing 13.6 shows the contents of validation.
xml as applied to RoadRantz.
If the contents of validation.xml look strangely familiar to you, it’s probably
because Struts uses the same validation file XML. Under the covers, Struts is using
Commons Validator to do its validation. Now Spring Modules brings the same
declarative validation to Spring.
One last thing to do is change the controller’s declaration to wire in the new
declarative implementation of Validator:
A basic assumption with SimpleFormController is that a form is a single page.
That may be fine when you’re doing something simple such as adding a rant. But
what if your forms are complex, requiring the user to answer several questions? In
that case, it may make sense to break the form into several subsections and walk
users through using a wizard. Let’s see how Spring MVC can help you construct
wizard forms.
13.3.3 Processing complex forms with wizards
Another feature of RoadRantz is that anyone can register as a user (known as a
motorist in RoadRantz’s terms) and be notified if any rants are entered for their
vehicles. We developed the rant notification email in chapter 12. But we also need
to provide a means for users to register themselves and their vehicles.
We could put the entire motorist registration form into a single JSP and extend
SimpleFormController to process and save the data. However, we don’t know
how many vehicles the user will be registering and it gets tricky to ask the user for
an unknown number of vehicle data in a single form.
Instead of creating one form, let’s break motorist registration into several subsections
and walk the user through the form using a wizard. Suppose that we partition
the registration process questions into three pages:
General user information such as first name, last name, password, and
email address
Vehicle information (state and plate number)
Confirmation (for the user to review before committing their information)
Fortunately, Spring MVC provides AbstractWizardFormController to help
out. AbstractWizardFormController is the most powerful of the controllers that
come with Spring. As illustrated in Figure 13.9, a wizard form controller is a special
type of form controller that collects form data from multiple pages into a single
command object for processing.
Let’s see how to build a multipage registration form using AbstractWizardFormController.
Building a basic wizard controller
To construct a wizard controller, you must extend the AbstractWizardFormController
class. MotoristRegistrationController (Listing 13.7) shows a minimal
wizard controller to be used for registering a user in RoadRantz.
Just as with any command controller, you must set the command class when using
a wizard controller. Here MotoristRegistrationController has been set to use
Motorist as the command class. But because the motorist will also be registering
one or more vehicles, the formBackingObject() method is overridden to set the
vehicles property with a list of Vehicle objects. The list is also started with a
blank Vehicle object for the form to populate.
Since the user can register any number of vehicles and since the vehicles list
will grow with each vehicle added, the form view needs a way of knowing which
entry in the list is the next entry. So, referenceData() is overridden to make the
index of the next vehicle available to the form.
The only compulsory method of AbstractWizardFormController is processFinish(). This method is called to finalize the form when the user has finished
completing it (presumably by clicking a Finish button). In MotoristRegistrationController, processFinish() sends the data in the Motorist object to
addMotorist() on the injected RantService object.
Notice there’s nothing in MotoristRegistrationController that gives any
indication of what pages make up the form or in what order the pages appear.
That’s because AbstractWizardFormController handles most of the work
involved to manage the workflow of the wizard under the covers. But how does
AbstractWizardFormController know what pages make up the form?
Some of this may become more apparent when you see how MotoristRegistrationController
is declared in roadrantz-servlet.xml:
So that the wizard knows which pages make up the form, a list of logical view
names is given to the pages property. These names will ultimately be resolved into
a View object by a view resolver (see section 13.4). But for now, just assume that
these names will be resolved into the base filename of a JSP.
While this clears up how MotoristRegistrationController knows which
pages to show, it doesn’t tell us how it knows what order to show them in.
Stepping through form pages
The first page to be shown in any wizard controller will be the first page in the list
given to the pages property. In the case of the motorist registration wizard, the
first page shown will be the motoristDetailForm page.
To determine which page to go to next, AbstractWizardFormController consults
its getTargetPage() method. This method returns an int, which is an index
into the zero-based list of pages given to the pages property.
The default implementation of getTargetPage() determines which page to go
to next based on a parameter in the request whose name begins with _target and
ends with a number. getTargetPage() removes the _target prefix from the
parameter and uses the remaining number as an index into the pages list. For
example, if the request has a parameter whose name is _target2, the user will be
taken to the page rendered by the motoristConfirmation view.
Knowing how getTargetPage() works helps you to know how to construct
your Next and Back buttons in your wizard’s HTML pages. For example, suppose
that your user is on the motoristVehicleForm page (index = 1). To create Next
and Back buttons on the page, all you must do is create submit buttons that are
appropriately named with the _target prefix:
When the Back button is clicked, a parameter with its name, _target0, is placed
into the request back to MotoristRegistrationController. The getTargetPage()
method will process this parameter’s name and send the user to the
motoristDetailForm page (index = 0). Likewise, if the Next button is clicked,
getTargetPage() will process a parameter named _target2 and decide to send
the user to the motoristConfirmation page (index = 2).
The default behavior of getTargetPage() is sufficient for most projects. However,
if you would like to define a custom workflow for your wizard, you may override
this method.
Finishing the wizard
That explains how to step back and forth through a wizard form. But how can you
tell the controller that you have finished and that the processFinish() method
should be called?
There’s another special request parameter called _finish that indicates to
AbstractWizardFormController that the user has finished filling out the form
and wants to submit the information for processing. Just like the _targetX parameters,
_finish can be used to create a Finish button on the page:
When AbstractWizardFormController sees the _finish parameter in the
request, it will pass control to the processFinish() method for final processing
of the form.
Unlike other form controllers, AbstractWizardFormController doesn’t provide
a means for setting the success view page. So, we’ve added a getSuccessView() method in MotoristRegistrationController to return the last page in
the pages list. So, when the form has been submitted as finished, the processFinish() method returns a ModelAndView with the last view in the pages list as
the view.
Canceling the wizard
What if your user is partially through with registration and decides that they don’t
want to complete it at this time? How can they abandon their input without finishing
the form?
Aside from the obvious answer—they could close their browser—you can add a
Cancel button to the form:
As you can see, a Cancel button should have _cancel as its name so that, when
clicked, the browser will place a parameter into the request called _cancel. When
AbstractWizardFormController receives this parameter, it will pass control to
the processCancel() method.
By default, processCancel() throws an exception indicating that the cancel
operation is not supported. So, you’ll need to override this method so that it (at a
minimum) sends the user to whatever page you’d like them to go to when they
click Cancel. The following implementation of processCancel() sends the user
to the success view:
If there is any cleanup work to perform upon a cancel, you could also place that
code in the processCancel() method before the ModelAndView is returned.
Validating a wizard form a page at a time
As with any command controller, the data in a wizard controller’s command
object can be validated using a Validator object. However, there’s a slight twist.
With other command controllers, the command object is completely populated
at once. But with wizard controllers, the command object is populated a bit
at a time as the user steps through the wizard’s pages. With a wizard, it doesn’t
make much sense to validate all at once because if you validate too early, you will
probably find validation problems that stem from the fact that the user isn’t finished
with the wizard. Conversely, it is too late to validate when the Finish button
is clicked because any errors found may span multiple pages (which form page
should the user go back to?).
Instead of validating the command object all at once, wizard controllers validate
the command object a page at a time. This is done every time that a page
transition occurs by calling the validatePage() method. The default implementation
of validatePage() is empty (i.e., no validation), but you can override it to
do your bidding.
To illustrate, on the motoristDetailForm page you ask the user for their email
address. This field is optional, but if it is entered, it should be in a valid email
address format. The following validatePage() method shows how to validate the
email address when the user transitions away from the motoristDetailForm page:
When the user transitions from the motoristDetailForm page (index = 0), the
validatePage() method will be called with 0 passed in to the page argument.
The first thing validatePage() does is get a reference to the Motorist command
object and a reference to the MotoristValidator object. Because there’s no need
to do email validation from any other page, validatePage() checks to see that
the user is coming from page 0.
At this point, you could perform the email validation directly in the validatePage()
method. However, a typical wizard will have several fields that will
need to be validated. As such, the validatePage() method can become quite
unwieldy. We recommend that you delegate responsibility for validation to a finegrained
field-level validation method in the controller’s Validator object, as
we’ve done here with the call to MotoristValidator’s validateEmail() method.
All of this implies that you’ll need to set the validator property when you configure
the controller:
It’s important to be aware that unlike the other command controllers, wizard controllers
never call the standard validate() method of their Validator object.
That’s because the validate() method validates the entire command object as a
whole, whereas it is understood that the command objects in a wizard will be validated
a page at a time.
The controllers you’ve seen up until now are all part of the same hierarchy
that is rooted with the Controller interface. Even though the controllers all get a
bit more complex (and more powerful) as you move down the hierarchy, all of
the controllers that implement the Controller interface are somewhat similar.
But before we end our discussion of controllers, let’s have a look at another controller
that’s very different than the others—the throwaway controller.
13.3.4 Working with throwaway controllers
One last controller that you may find useful is a throwaway controller. Despite the
dubious name, throwaway controllers can be quite useful and easy to use. Throwaway
controllers are significantly simpler than the other controllers, as evidenced
by the ThrowawayController interface:
public interface ThrowawayController {
ModelAndView execute() throws Exception;
}
To create your own throwaway controller, all you must do is implement this interface
and place the program logic in the execute() method. Quite simple, isn’t it?
But hold on. How are parameters passed to the controller? The execution
methods of the other controllers are given HttpServletRequest and command
objects from which to pull the parameters. If the execute() method doesn’t take
any arguments, how can your controller process user input?
You may have noticed in Figure 13.5 that the ThrowawayController interface is
not even in the same hierarchy as the Controller interface. This is because
throwaway controllers are very different from the other controllers. Instead of
being given parameters through an HttpServletRequest or a command object,
throwaway controllers act as their own command object. If you have ever worked
with WebWork, this may seem quite natural because WebWork actions behave in a
similar way.
From the requirements for RoadRantz, we know that we’ll need to display a list
of rants for a given month, day, and year. We could implement this using a command
controller, as we did with RantsForVehicleController (Listing 13.3).
Unfortunately, no domain object exists that takes a month, day, and year. This
means we’d need to create a special command class to carry this data. It wouldn’t
be so hard to create such a POJO, but maybe there’s a better way.
Instead of implementing RantsForDayController as a command controller,
let’s implement it as a ThrowawayController, as shown in Listing 13.8.
Before RantsForDayController handles the request, Spring will call the setDay() method, passing in the value of the day request parameter. Once in the
execute() method, RantsForDayController simply passes day to rantService.
getRantsForDay() to retrieve the list of rants for that day. One thing that
remains the same as the other controllers is that the execute() method must
return a ModelAndView object when it has finished.
Just as with any controller, you also must declare throwaway controllers in the
DispatcherServlet’s context configuration file. There’s only one small difference,
as you can see in this configuration of RantsForDayController:
Notice that the scope attribute has been set to prototype. This is where throwaway
controllers get their name. By default all beans are singletons, and so unless
you set scope to prototype, RantsForDayController will end up being recycled
between requests. This means its properties (which should reflect the request
parameter values) may also be reused. Setting scope to prototype tells Spring to
throw the controller away after it has been used and to instantiate a fresh instance
for each request.
There’s just one more thing to be done before we can use our throwaway controller.
DispatcherServlet knows how to dispatch requests to controllers by
using a handler adapter. The concept of handler adapters is something that you
usually don’t need to worry about because DispatcherServlet uses a default handler
adapter that dispatches to controllers in the Controller interface hierarchy.
But because ThrowawayController isn’t in the same hierarchy as Controller,
DispatcherServlet doesn’t know how to talk to ThrowawayController. To make
it work, you must tell DispatcherServlet to use a different handler adapter. Specifically,
you must configure ThrowawayControllerHandlerAdapter as follows:
By just declaring this bean, you are telling DispatcherServlet to replace its
default handler adapter with ThrowawayControllerHandlerAdapter.
This is fine if your application is made up of nothing but throwaway controllers.
But the RoadRantz application will use both throwaway and regular controllers
alongside each other in the same application. Consequently, you still need
DispatcherServlet to use its regular handler adapter as well. Thus, you should
also declare SimpleControllerHandlerAdapter as follows:
Declaring both handler adapters lets you mix both types of controllers in the
same application.
Regardless of what functionality your controllers perform, ultimately they’ll
need to return some results to the user. The result pages are rendered by views,
which are selected by their logical name when creating a ModelAndView object.
But there needs to be a mechanism to map logical view names to the actual view
that will render the response. We’ll see that in chapter 14 when we turn our attention
to Spring’s view resolvers.
But first, did you notice that all of Spring MVC’s controllers have method signatures
that throw exceptions? It’s possible that things could go awry as a controller
processes a request. If an exception is thrown from a controller, what will the user
see? Let’s find out how to control the behavior of errant controllers with an
exception resolver.
13.4 HANDLING EXCEPTIONS
There’s a bumper sticker that says "Failure is not an option: it comes with the software."
Behind the humor of this message is a universal truth. Things don’t always
go well in software. When an error happens (and it inevitably will happen), do
you want your application’s users to see a stack trace or a friendlier message? How
can you gracefully communicate the error to your users?
SimpleMappingExceptionResolver comes to the rescue when an exception is
thrown from a controller. Use the following <bean> definition to configure SimpleMappingExceptionResolver
to gracefully handle any java.lang.Exceptions
thrown from Spring MVC controllers:
The exceptionMappings property takes a java.util.Properties that contains a
mapping of fully qualified exception class names to logical view names. In this
case, the base Exception class is mapped to the View whose logical name is
friendlyError so that if any errors are thrown, users won’t have to see an ugly
stack trace in their browser.
When a controller throws an Exception, SimpleMappingExceptionResolver
will resolve it to friendlyError, which in turn will be resolved to a View using
whatever view resolver(s) are configured. If the InternalResourceViewResolver
from section 13.4.1 is configured then perhaps the user will be sent to the page
defined in /WEB-INF/jsp/friendlyError.jsp.
13.5 SUMMARY
The Spring Framework comes with a powerful and flexible web framework that
is itself based on Spring’s tenets of loose coupling, dependency injection, and
extensibility.
At the beginning of a request, Spring offers a variety of handler mappings that
help to choose a controller to process the request. You are given a choice to map
URLs to controllers based on the controller bean’s name, a simple URL-to-controller
mapping, the controller class’s name, or source-level metadata.
To process a request, Spring provides a wide selection of controller classes with
complexity ranging from the very simple Controller interface all the way to the
very powerful wizard controller and several layers in between, letting you choose a
controller with an appropriate amount of power (and no more complexity than
required). This sets Spring apart from other MVC web frameworks such as Struts
and WebWork, where your choices are limited to only one or two Action classes.
All in all, Spring MVC maintains a loose coupling between how a controller is
chosen to handle a request and how a view is chosen to display output. This is a
powerful concept, allowing you to mix-’n’-match different Spring MVC parts to
build a web layer most appropriate to your application.
In this chapter, you’ve been taken on a whirlwind tour of how Spring MVC handles
requests. Along the way, you’ve also seen how most of the web layer of the
RoadRantz application is constructed.
Regardless of what functionality is provided by a controller, you’ll ultimately
want the results of the controller to be presented to the user. So, in the next chapter,
we’ll build on Spring MVC by creating the view layer of the RoadRantz application.
In addition to JSP, you’ll learn how to use alternate template languages such
as Velocity and FreeMarker. And you’ll also learn how to dynamically produce
non-HTML output such as Excel spreadsheets, PDF documents, and RSS feeds.
Master SharePoint with 3 eLearning Seminars Learn how to build a better SharePoint infrastructure and enable powerful collaboration with MVPs Dan Holme and Michael Noel. Register today!
SharePointConnections Conference Fall 2008 Don’t miss the premier event for Microsoft IT Professionals in Las Vegas, November 10-13. Register and book your room by August 25 and receive a FREE room night (based on a three night minimum stay).
VMworld 2008 - Sign Up Today! Join your peers on September 15-18 at The Venetian Hotel in Las Vegas as VMware hosts VMworld 2008, the leading Virtualization event.
Microsoft® Tech•Ed EMEA 2008 IT Professionals Advance your thinking with new ideas and practical real-world solutions at Microsoft’s FIVE day technical infrastructure conference 3-7 Nov., 2008. Register before 26 September 2008 to save €300.
Order Your SQL Fundamentals CD Today! Learn how to use SQL Server, understand Office integration techniques and dive into the essentials of SQL Express and Visual Basic with this free SQL Fundamentals CD.
Are You Really Compliant with Software Regulations? View this web seminar that will help you with compliance best practices and check out a management solution to assure that you won’t be in jeopardy of an audit.