Now that you've learned about the basic concepts involved with implementing a web application using Spring Web MVC, let's get into the details.
Implementing a web application using Spring Web MVC involves building and setting up controllers, providing views and data to display in the views. All this is linked together using a DispatcherServlet in combination with an ApplicationContext. The DispatcherServlet provides a gateway to the server and acts as the manager of the workflow involved with HTTP-based request-response handling. A special type of context, the WebApplicationContext integrates with the dispatcher servlet and manages all web-related components such as controllers, views, mappings between URLs, and interceptors. The WebApplicationContext doesn't differ much from a "normal" ApplicationContext, and as a user, you'll hardly notice the difference.
Figure 12-3 lays out the basic architecture involved with a Spring web application. Next, we'll discuss the components in more detail.
The org.springframework.web.servlet.DispatcherServlet is the main entry point of any request to a Spring MVC application. Like any other servlet, the dispatcher servlet must be declared in your web.xml file. This makes it available to the outside world (meaning it is accessible to applications using the HTTP protocol). The servlet can be configured to meet your needs and has the initialization parameters listed in the table that follows to control its behavior.
Note that in all but exceptional situations the default parameters to DispatcherServlet will do. In addition, you shouldn’t need to subclass DispatcherServlet to provide custom behavior. In the case of Spring MVC, like Spring generally, the framework provides a host of extension points in the form of interfaces that you can implement. We’ll see most of these later in this chapter.
You should not normally need to extend Spring infrastructure classes to customize behavior, which ties your code unnecessarily to the implementation of the framework itself.
Type of WebApplicationContext to use
The namespace of the WebApplicationContext to use
[name of servlet]-servlet
Parameter to override the default value of location(s) to look for WebApplicationContext(s)
Whether or not to publish to ApplicationContext in the ServletContext (making it available to other Servlets)
Each DispatcherServlet defined in a Spring web application will have an associated WebApplicationContext. As we've seen, by default its namespace will include the name of the servlet, and its scope is limited to requests mapped to that servlet only, meaning other servlets receiving a request won't be able to use objects defined in WebApplicationContext initialized by the first one.
To be able to share common functionality across multiple servlets within one application, upon initialization, the dispatcher servlet will first of all inspect its configuration (elements of type init-param elements from web.xml), and start looking for an "ordinary" ApplicationContext, initialized by a ContextLoaderListener or ContextLoaderServlet, as described in Chapter 3. This context might, for instance, contain the middle-tier services that you're going to use from your controllers. If you use Spring's built-in, web-oriented ContextLoader, such an application context must be located in the ServletContext of the web application. If such an ApplicationContext is found, it will be the parent of the WebApplicationContext that will be tied specifically to the servlet.
A Spring Web MVC application cannot exist without a WebApplicationContext. When using the DispatcherServlet's default configuration, you must provide an XML file defining beans, place it in the /WEB-INF directory of your WAR file, and name it [name of servlet]-servlet.xml unless you specify otherwise.
As already mentioned, it's possible to have multiple DispatcherServlets for one web application. This approach has a similar result as Struts modules. Think carefully about where to place your beans. The xxx–servlet.xml should always contain web-related beans, while the parent application context loaded by the ContextLoaderListener, for example, should contain middle-tier beans, such as datasources, data access objects, and service objects.
You declare a Spring DispatcherServlet in your web.xml file as follows. This servlet will use default values for all configuration parameters, resulting in a web application context defining controllers and other web tier objects being loaded from /WEB-INF/sample-servlet.xml.
<servlet> <servlet-name>sample</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>2</load-on-startup> </servlet>
To illustrate how to modify the behavior of the DispatcherServlet, you could have two XML files, together defining a WebApplicationContext. To do this, you would have to include an init-param in the declaration of the Servlet. Multiple locations can be included by separating those using commas, semicolons, or spaces.
<init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/ctx/controllers.xml /WEB-INF/ctx/utils.xml</param-value> </init-param>
Next, you have to tell the Servlet container to direct requests you want handled by your Spring web application to the DispatcherServlet. You usually map a range of file extensions to the DispatcherServlet, for example all files ending with .view and .edit, in your web application context mapped to view controllers and form controllers. You map requests to the DispatcherServlet using Servlet mappings. The Servlet mapping that follows will tell the servlet container to mapall requests ending with .view and all requests ending with .edit in the secure path to the DispatcherServlet. Note that this configuration step is required by the Servlet specification, notby Spring.
<servlet-mapping> <servlet-name>sample</servlet-name> <url-pattern>*.view</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>sample</servlet-name> <url-pattern>/secure/*.edit</url-pattern> </servlet-mapping>
The previously mentioned URLs clearly indicate what they're dealing with: editing and viewing. In addition to a clear separation based on functionality, a well-defined and logically grouped collection of URLs in your web application has more benefits. In one project one of the authors encountered a situation where he needed to apply a SiteMesh page decorator to all requests, except for the ones that were targeted to a new window (pop-ups). Using a clearly defined grouping, he could easily map the decorator to all requests, except for those ending with .popup.
Neither Spring nor the Servlet API forces you to use specific extensions or patterns in URLs. You are free to use whatever URLs you like. Even better, with Spring, matching of controllers need not be based purely on URLs — a capability not supported by most web MVC frameworks. For example, you could define a mapping based on parameters, or even HTTP session state.
Remember that exposing extensions such as .jsp or .asp — or even the .do used by convention in many Model 2 Java web applications — gives potential hackers immediate insight into what technology you're using. If you can't come up with a sensible extension, consider using .html instead.
After you have declared the DispatcherServlet and possibly modified it using Servlet initparams, you're finished as far as the web.xml file is concerned. Next, you will have to create a WebApplicationContext containing controllers and possibly other web-related components such as view resolvers, exception handlers, and locale resolvers. If you are happy with the defaults, most of these definitions are optional. However, by defining them you can achieve a high degree of customization of MVC framework behavior, if required.
Remember that initializing a separate application context containing your middle-tier services should be done by using the ContextLoaderServlet (or Listener). This is already covered in Chapter 3, but we'll include a short code snippet to refresh your memory:
<context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/applicationContext.xml </param-value> </context-param> <servlet> <servlet-name>context</servlet-name> <servlet-class> org.springframework.web.context.ContextLoaderServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet>
Remember that if using the ContextLoaderServlet, don't forget to add the load-on-startup parameter. The parent application context (the one for your middle-tier services) should be loaded first (hence you have a lower load-on-startup figure), before the DispatcherServlet, which usually needs beans from the parent context!
The WebApplicationContext contains all web-related beans responsible for making your logic accessible to users. These may include beans to handle multilingual websites and beans defining mappings from incoming requests to controllers. The WebApplicationContext will be initialized by the DispatcherServlet. When using default values, it will be of class org.springframework.web.context.support.XmlWebApplicationContext.
The WebApplicationContext is a child context of the ApplicationContext, which will contain middle-tier services such as transactional service layer objects, data sources, and data access objects.
It's a best practice to keep a clear separation between middle-tier services and web- related components. A web interface is just one way to access middle-tier services. Remoting or JMS might be another way. In the case of remoting, there will be an additional child context, containing all beans necessary to access the service by, for example, JAX-RPC.
The XmlWebApplicationContext is used by the DispatcherServlet to model the workflow involved with web applications. The DispatcherServlet inspects the WebApplicationContext for declarations of some special beans used to execute the workflow. Some of them will have defaults; some of them you will have to provide yourself. You will learn more about those types of beans later, but let's now have a quick look at what they are:
A HandlerMapping is responsible for determining the appropriate controller (or any other custom handler) to dispatch a request to. A criterion to base the decision on might be, for example, the URL of the request.
A ViewResolver is capable of mapping logical view names to actual views, such as a JavaServer Page, a Velocity template, or an XSL style sheet to apply to the contents of the model.
A LocaleResolver will resolve the locale to use when rendering a view or performing other language-dependent logic.
A MultipartResolver provides support for uploading files with HTTP, for instance by using Commons FileUpload.
ThemeResolvers provide support for themeable interfaces, for example, by using different style sheets and resource bundles.
HandlerExceptionResolvers provide support for catching unexpected exceptions and, for example, informing the user by providing a predefined view displaying information about the exception.
Controllers provide the interactional logic of your web application and access to collaborators from your middle tier, which provide actual business services, which should be independent of any particular interface type.
HandlerInterceptors provide the capability to intercept incoming HTTP requests. Interceptors are useful to add additional crosscutting behavior to your web infrastructure, such as security, logging, and auditing.
As you can see, Spring MVC offers a host of extension points — which, we feel, makes it uniquely flexible among web application frameworks. Note that you don't need to specify custom implementations of these extension points unless you need the extra power they provide. So don't worry if you're feeling a bit overwhelmed right now — you don't need to deal with all these concepts in simple applications, but you will be very grateful for the flexibility they offer as your web applications become more complex.
WebApplicationContext inherits all functionality of the ApplicationContext (as discussed in Chapter 2). Beyond that, it holds a reference to the ServletContext. In normal situations, you don’t need access to the WebApplicationContext or the ServletContext from your controllers or other web-related beans. If it is necessary, you can extend the org.springframework.web.context.support.WebApplicationObjectSupport convenience class, which in turn extends ApplicationObjectSupport (see Chapter 2). WebApplicationObjectSupport provides the following methods:
WebApplicationContext getWebApplicationContext(); ServletContext getServletContext(); File getTempDir();
The first method returns the application context in which your web-related beans reside. In common situations you usually won't need access to the application context because wiring up collaborating beans should preferably be done in the file describing the WebApplicationContext.
The getServletContext() method provides access to the ServletContext through which you can reach web application–wide resources possibly stored in there by other servlets.
If you need a place to store temporary files, Servlet containers offer a standard facility and getTempDir() returns a directory where you can store your information. This is, for example, often used by utilities that enable users to upload files. The file is first stored in a temporary directory after which it sits there waiting to be processed by a controller.