We've discussed locale resolution, model-and-view classes, view resolution, and mapping URLs to handlers, but what about the controllers themselves? Spring provides a variety of controllers that will meet your needs when implementing important common requirements in web applications. For example, Spring features form controllers allowing you to do form workflow, command controllers, and multi- action controllers, comparable to Struts DispatchActions. We'll discuss the most important controllers before covering usage scenarios in which we'll not only discuss the controllers, but also all the other features you've learned about earlier in this chapter.
Spring's most important controllers implement the org.springframework.web.servlet.support.WebContentGenerator interface. The WebContentGenerator also serves as a basis for the WebContentInterceptor, which has already been discussed along with the HandlerMapping earlier in this chapter. There you can also find a detailed explanation of the parameter the WebContentGenerator offers.
Spring's most basic controller is the org.springframework.web.servlet.mvc.AbstractController. It provides an abstract implementation of the Controller interface mentioned earlier in this chapter. Common functionality shared across all controllers is implemented here, and together with theorg.springframework.web.servlet.support.WebContentGenerator, it provides a good foundation for controllers, in the end resulting in web pages or other HTTP-related content. Remember that often you can use a controller further down the hierarchy that offers behavior more targeted at a specific requirement. There will, however, be situations in which you need to extend AbstractController. The only method you need to implement returns a ModelAndView:
protected ModelAndView handleRequestInternal( HttpServletRequest request, HttpServletResponse response) throws Exception;
Important | Remember that you can also return null from your controller, in which case Spring will assume you have dealt with the request yourself (for example by writing to HttpServletResponse). This feature should be used with caution, however. You typically don’t want to end up outputting HTML in your controller. Use this feature only if you want to redirect the user to a different page in rare cases where you can’t handle the situation with an exception resolver — or perhaps, if you want to generate binary content in the controller itself. (Note that the latter is not usually necessary, as a View would normally be a better choice. The Spring view abstraction can cope with generating any content type, including binary content types, as it is not tied to JSP or HTML.) |
Let's start with simple controllers, which do not involve logic such as form handling. As we've seen, a Controller should return an appropriate ModelAndView after processing a request. The View will be used to render a response to the client; the model is data relevant to the request. In some cases no model data is required, and you simply want to render a view (a plain JSP or Velocity template not incorporating any dynamic data). In this case you can use the org.springframework.web.servlet.mvc.UrlFilenameViewController. It transforms the virtual filename in the URL to a logical view name that will be included in the ModelAndView, returned, and then picked up by any ViewResolver you're using. This is a convenient controller to let all requests go through Spring, and, for example, expose JSPs located in your /WEB-INF directory indirectly to your users. We'll see an example of using the UrlFilenameViewController in the next chapter when we discuss Tiles integration.
Important | Using the UrlFilenameViewController can be especially convenient in case you have static resources such as HTML pages but still need every request intercepted by a HandlerInterceptor. In any case, try to avoid calling JSP pages directly; here the UrlFilenameViewController comes in handy as well. |
Let's have a look at an example:
<bean /> <bean > <property name="interceptors"> <list> <ref bean="loggingInterceptor"/> </list> </property> <property name="urlMap"> <map> <entry key="/*.html"><ref local="staticViewController"/></entry> </map> </property> </bean> <bean > <property name="suffix"><value>.jsp</value></property> <property name="prefix"><value>/WEB-INF/jsp/</value></property> <property name="viewClass"> <value>org.springframework.web.servlet.view.JstlView</value> </property> </bean>
Using an InternalResourceResolver in combination with a UrlFilenameViewController will map all requests for HTML files to the JSPs in the WEB-INF/jsp directory. In the example, we apply a logging interceptor to every request.
The org.springframework.web.servlet.mvc.ParameterizableViewController is a simple controller that allows easy configuration of the view to return. It defines a template method called handleRequestInternal() that does the actual work, which is not much. On its own, the controller does no more than just returning the view you've been configuring using the viewName property. Of course, you can extend the controller if you like and implement different behavior in an overridden handleRequestInternal() method. The advantage this controller has over the AbstractController is that is decouples the view from the controller:
<bean name="/simple.html" > <property name="viewName"><value>welcome</value></property> </bean>
On the one hand, it's convenient to be able to configure your view outside the controller itself, but on the other hand, if you forget to configure it, the view name will be "null," which is not allowed!
The MultiActionController provides a component with which you'll be able to group certain functionality mapped to different requests and request types in one controller. The MultiActionController resembles (and is indebted to) the Struts 1.1 DispatchAction and MappingDispatchAction, but in true
Spring fashion, it is more configurable and interface-friendly. Remember that a MultiActionController is not the only way of preventing common functionality to be duplicated. Other options to allow for reusable functionality used by multiple controllers include simply using plain old inheritance where shared functionality is put in superclasses.
The multiple actions in a MultiActionController are modeled as methods in any deriving controller. An example:
import org.springframework.web.servlet.mvc.multiaction.MultiActionController; public class TicketController extends MultiActionController public ModelAndView reserve(HttpServletRequest request) { // perform logic to reserve a ticket based on the // request coming in... } public ModelAndView cancel(HttpServletRequest request) { // perform logic to cancel a reservation based on the // request coming in... } }
The MultiActionController works only in combination with a MethodNameResolver. Based on the incoming HTTP request, the resolver will decide what method of the MultiActionController will take care of the actual processing and creation of a ModelAndView. Built-in MethodNameResolvers include the ParameterMethodNameResolver, inspecting the HttpServletRequest for a preconfigured parameter containing the name of the method to call, and the PropertiesMethodNamesResolver that uses a Properties mapping and Ant-style expressions to resolve a method name. Configuring a method name resolver in combination with a MultiActionController would be done as follows:
<bean > <property name="methodNameResolver"> <ref local="reservationActions"/> </property> </bean> <bean > <property name="mappings"> <props> <prop key="/reserve/*.ticket">reserve</prop> <prop key="/cancel/*.ticket">cancel</prop> </props> </property> </bean>