Let's begin by exploring the concepts involved with applying MVC to the web. If you've worked with WebWork or Struts or some other web MVC framework before, you will recognize many of the concepts discussed in the next few pages. However, even if the concepts are familiar, the following discussion should provide a useful insight into the rationale behind the architecture of Spring's Web MVC Framework.
The MVC architectural pattern aims to divide the layer implementing interactions with users into three different kinds of objects:
Model objects represent data: for example, the orders a user has placed, or information about a user's account.
View objects are responsible for displaying the data represented by the model: for instance, in a table, list, or form.
Controllers are objects responding to the actions a user takes, such as a form submission or a link being clicked. Controllers are responsible for updating the data represented by the model or taking other appropriate measures related to the user's action. This might for example involve delegating a request to the business tier that in turn will process an order.
The traditional implementation of the MVC architectural pattern in GUI implementation (depicted in Figure 12-1) involves actions being taken, after which controllers update the model, with the changes being pushed back to the view, which updates itself (through for example the Observer pattern). Such an ideal implementation is not feasible for web applications because of the limitations of HTTP. Typically, a user issues a request, allowing the server to update the view, which is then sent back to the user's browser. It's impossible to propagate changes back to the client in between requests. This forces us to use an approach where instead of the model pushing changes to the view, on every request, the view pulls the relevant data from the model in order to display it in tables, lists, forms, or whatever user interface elements are required.
The MVC design pattern defines a clear separation between responsibilities and results in clean code. Fortunately, using a pull approach will not prevent you from using the MVC triad. It just changes the way things work a bit. So in Spring MVC you will still be able to separate logic accessing data from the data itself and the code that will render the view. The latter might be an HTML page generated by JavaServer Pages (JSP) or an Excel file generated by a class using Apache POI.
Let's now take a closer look at web applications designed using the MVC pattern. A web browser uses an HTTP request to communicate a user's action to the server. The server does some processing and generates a view, which is communicated back to the client using an HTTP response.
The MVC pattern comes into play when the HTTP request reaches the server. Based on the information carried in the HTTP request, such as the URL and any parameters, the web application should be able to execute it. When it has finished processing the user's request, possibly creating or updating a model, it will hand over responsibility to the view. The view in its turn uses the HTTP response to transmit any data back to the client, such as pages marked up using HTML or raw data representing an Excel document.
A good web framework distinguishes between dispatching the request and processing it. In other words, the dispatcher determines what controller is needed to fulfill each request while the controller does the actual processing. This separation removes the need for unnecessary processing of the HTTP request when implementing controllers, making application code cleaner and less closely coupled to HTTP. Such decoupling increases testability and may allow for greater code reusability.
Figure 12-2 illustrates how a request will be handled by a web framework using the concepts described previously.
The MVC pattern applied to web applications is discussed in greater detail in Chapter 12 of Expert Oneon-One J2EE Design and Development.
Note that a true MVC pattern is based on event notification — it's a push model where changes to the model get propagated to the view using the Observer pattern. MVC applied to web applications is also known as the Model 2 architecture. Some purists argue that because of missing out on the push aspect of the true Model-View-Controller pattern, the Model 2 architecture shouldn't be called MVC. Nevertheless, the term "web MVC" is here to stay. What matters is that a good Model 2 implementation provides a clear and fixed decomposition of all the aspects involved with the creation of a web application. It provides the best separation of concerns possible within the limits of the push-based approach of modern-age browsers.
Spring does not try to reinvent the wheel. It unites and integrates best practices and solutions: for example, Hibernate and iBatis, in the case of data access. However, Spring does provide its own MVC package. Given the existence of other popular MVC implementations such as Struts and WebWork, you might wonder why.
The rationale behind implementing yet another MVC implementation lies in the fact that we considered that other more traditional implementations didn't provide an elegant enough solution. Spring has clearly set a couple of goals for the MVC package (a more thorough discussion can be found in Johnson's J2EE Design and Development):
An MVC implementation should be non-intrusive. Where possible, you shouldn't have to adopt practices or standards set by the developers of the MVC framework you are using. Typical examples here would include the requirement to extend framework-proprietary classes to be able to do dynamic binding of request parameters to domain objects. Also, you shouldn't have to do this to create controllers implementing your logic. Finally, validation shouldn't be tied to the front-end; you might prefer to do this in your business logic. Spring uses interfaces where possible; you will not be restricted to using functionality provided only by the framework, and you can easily implement additional behavior yourself.
Across web applications developed over time, differences might exist with respect to a view technology being used, performance requirements, or the way the framework determines which controller is used to fulfill a certain request. An MVC framework should be suited for environments having different requirements. It should, for example, be possible to implement a high- performance controller that implements only the bare necessities as well as controllers that execute wizard-style workflow including validation, error-messaging, data binding, and detection for invalid form submissions.
An MVC package should be agnostic with respect to the view technology being used. Controller implementations or the contents of a model shouldn't have to change when switching from JavaServer Pages to Velocity, or having both an HTML view as well as a view rendering a PDF file representing the data in the model. It should also be possible to easily include new view technologies being developed in the community.
Complete freedom should be maintained with respect to HTML generation. Simple and small utilities (such as custom tag libraries) should be provided only for functionality the framework contains that can't be used without those utilities.
The framework should be highly configurable, consistent with the way this is done throughout the complete solution. The way you manage and configure middle-tier services shouldn't be any different from wiring up controllers, views, and additional web-related functionality. All the benefits of Dependency Injection should be applied to the web tier.
We encourage you to keep the MVC layer as thin as possible, by providing easy ways to delegate requests to the middle-tier and integrating with other services such as AOP.
These requirements partly came out of Rod Johnson's experience architecting demanding high-end web applications from 1999 to 2001. All were practical, rather than theoretical, requirements. They have proven to recur in many user requirements we have seen since.
In the previous sections you have learned about models, views, controllers, and dispatchers. Spring provides equivalents to those that can be completely integrated with your middle-tier code using Spring's Dependency Injection features.
The components actually taking care of web-related processing (and eventually delegating tasks to your middle tier) are the controllers. Spring includes a variety of controllers, all targeted to solve specific problems in web applications such as form handling and doing wizard-style workflow. Most of the controllers Spring provides derive from the org.springframework.web.servlet.mvc.Controller interface, which specifies the following method:
ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse res) throws Exception
A controller is responsible for handling the request through either returning a ModelAndView relevant to the request (the normal case), or returning null if the controller has dealt with the request itself: that is, has written to the ServletResponse itself — generally not considered to be a good practice, but sometimes unavoidable. Spring provides several controller implementations, many of which don't work with the HttpServletResponse object. As a developer, you probably won't ever need to work with the servlet response, as the ModelAndView gives you all the control you need. But if you ever stumble upon a situation where you need more flexibility, you can always implement the Controller interface or extend one of the controllers somewhat higher up in the hierarchy (such as AbstractController).
The ModelAndView class allows you to specify a response to the client, resulting from actions you have performed by the controller. This object holds both the view the client will be presented with as well as the model that will be used to render the view. ModelAndView objects can be created in a number of ways. The underlying structure of the model is a java.util.Map. A convenient constructor allows you to specify a model map directly, or you can construct an empty model and add objects through methods on the ModelAndView class itself:
// first create ModelAndView, along with the name of the view ModelAndView mav = new ModelAndView("showDetailsTemplate"); // add the show which we’ll be mav.addObject("show", showDao.getShow()); return mav;
A request in Spring, dispatched to a controller, should always result in a ModelAndView object being created and returned, unless you’ve handled the request directly and do not wish the dispatcher to start rendering any view.
One might ask why Spring incorporates its own ModelAndView type. Model elements are added to the map this type holds, and adding elements directly to the HttpServletRequest (as request attributes) would seem to serve the same purpose. The reason to do this is obvious when taking a look at one of the requirements we have set for the MVC framework: It should be as view-agnostic as possible, which
means we'd like to be able to incorporate view technologies not bound to the HttpServletRequest as well. This could be, for example, XSLT where the model is contained by an XML document that is to be transformed using an XSL style sheet. Testability is also slightly improved by abstracting away from the Servlet API.
To complete the picture, you should understand the View interface and the concept of view resolvers, although you'll probably never implement a View directly. Implementations of the View interface are responsible for rendering the response to the client, often being something like an HTML page. The View interface provides exactly one method stating its responsibility: rendering the view:
void render(Map model, HttpServletRequest req, HttpServletResponse res) throws Exception
Implementations of the View interface will typically evaluate a template such as a JSP or Velocity template to generate actual content, rather than doing it in their own Java code. Thus View implementations are often generic, parameterized by their use with many different templates.
Spring uses a view resolution strategy involving logical view names. Views are identified by name, keeping controllers from having to specify identifiers specific to the view technology being used. A view resolver is responsible for delivering a view technology–specific view object, based on the logical view name. Using view technology–independent logical view names in conjunction with a view resolution strategy provides complete decoupling of controllers returning models and views rendering models. Switching from one view technology to another does not require you to change any controllers or other infrastructural code — merely configuration and files specific to the view technology being used such as JSPs or Velocity templates.
As we've discussed the general concept of web MVC, you've heard about requests being dispatched to controllers. Typically, web frameworks implement this behavior of dispatching the HTTP request using a Front Controller Servlet. So does Spring. The main entry point for any request a user is issuing is the DispatcherServlet. This will inspect it and determine what controller it needs to execute the given request. The DispatcherServlet models the workflow involved with executing a request. It dispatches requests to controllers and takes care of rendering the response, based on the model and view information the controller returns. All this is done using a couple of other features of Spring Web MVC, which you'll learn about later on.