HandlerMappings


You've seen a web.xml file defining URL patterns. All URLs matching those patterns will be routed to the Spring DispatchetServlet and picked by one or more HandlerMappings. A HandlerMapping is capable of determining a path of execution through the web application. As we've seen, the execution chain consists of one handler and possibly one or more interceptors.

The determination of the path of execution can be done using a variety of criteria. The most obvious way to determine which handler to use is to inspect the URL of the request. Using Spring, however, you're not limited to using the URL. If you want, you can implement a handler mapping yourself, whichcan be done by deriving from AbstractHandlerMapping and overriding getHandlerInternal(HttpServletRequest) or implementing the HandlerMapping interface.

In your application context, you can define more than one handler mapping if you need to. When doing this, Spring will inspect each one of them until an appropriate path of execution has been determined. The HandlerMapping implements the Ordered interface, meaning that at startup the context will inspect all handler mappings and order them using the OrderComparator (this is explained in more detail in Chapter 2).

Important 

If after querying all HandlerMappings available, a HandlerExecutionChain still isn't found — that is, no matching handler is found — Spring will inform the client by sending it an HTTP 404 response code (Page Not Found).

Note that you don't necessarily have to define a handler mapping in your application context. If Spring doesn't find one in your context, it will use the BeanNameUrlHandlerMapping.

Spring provides handler mappings using typical criteria to determine the path of execution. Let's examine them.

BeanNameUrlHandlerMapping

Using the BeanNameUrlHandlerMapping you can map URLs to Handlers by using their names in the web application context. Consider the following (part of a) web application context:

<bean    name="/account.edit /secure/*account.edit"    />

This specific controller will be used to dispatch all requests targeted to /account.edit, /secure/ largeaccount.edit, and /secure/smallaccount.edit.

Important 

If you don't define a handler mapping in the web application context yourself, BeanNameUrlHandlerMapping will be used.

This controller is most similar to the behavior Struts provides for mapping URLs to Struts Actions. You can list more than one URL, and you can define URL patterns, using an Ant-style notation.

To use BeanNameUrlHandlerMapping effectively, you must understand Ant-style path expressions. To be able to match a path using a generic expression, Ant uses asterisks and question marks. When evaluating a path expression, the question marks act like wildcards and will cause a path to match, no matter what character is in place of the question mark as long as there is just one character in that specific place. This means that, for example, given the path expression /secure/test.?sp, both /secure/test.jsp and /secure/test.asp will match.

In addition to a question mark, you can use the asterisk, which also acts like a wildcard, but can be replaced by any given number of characters or even no character at all. This means that both /secure/simpletest.jsp and /secure/test.jsp will match when evaluating those paths using /secure/*test.jsp as the path expression. When using two asterisks, you indicate you want URLs in all paths to match. For example, when using /**/test.jsp as a path expression, both /secure/test.jsp and /nonsecure/deeper/test.jsp will match, as well as /test.jsp.

SimpleUrlHandlerMapping

In addition to BeanNameUrlHandlerMapping, Spring provides another handler mapping that can be configured in the application context, which will use the request URL to base a mapping on. Using Ant-style path expressions and the SimpleUrlHandlerMapping, you can create more complex URL structures that map to handlers and have other more advanced features available. Consider the following examples:

<bean  />     <bean     >   <property name="urlMap">     <map>       <entry key="/secure/account*.edit">         <ref bean="accountEditor"/>       </entry>     </map>   </property> </bean>

The above code snippet shows a controller responsible for editing accounts. The controller is mapped to all incoming requests URLs matching the given pattern (/secure/account*.edit).

In the preceding example, we're using the urlMap property to include a mapping of URL patterns to controllers. We could also have used the mappings property. The latter does not support references to a controller directly, as done in the preceding example. Instead it maps URLs or URL patterns to bean names using a Properties object. This gives you the ability to externalize the mapping in a file if you want to. Let's have a look at that:

<bean  /> <bean  />     <bean     >   <property name="mappings">     <bean            >       <property name="location">         <value>urlmap.properties</value>       </property>     </bean>   </property> </bean>     ## urlmap.properties file located in /WEB-INF/ /secure/account*.edit=accountEditor /**/account.view=accountViewer 

Important 

By default, Spring treats all controllers as singletons. In some situations (where you explicitly reference beans, using <ref bean="XXX"/>, even setting the singleton property of the given bean to false won't result in multiple beans being created (for that specific reference). A solution to this problem in case of the SimpleUrlHandlerMapping is to use the mappings property, not referring directly to beans, but identifying a controller by name. Every time Spring needs a controller included in the mapping, it will ask the ApplicationContext for one (hence respecting the singleton property). In other words, if you want to model a controller as a prototype (which is generally discouraged), use the mappings property containing bean names and set the singleton property (of the controller) to false. This allows a single use controller — for example, one that may not be threadsafe.

CommonsPathMapUrlHandlerMapping

Spring also provides a handler mapping that works with Commons Attributes, defined in your controllers. Metadata and attributes are more extensively covered in Chapter 9.

Using the CommonsPathMapUrlHandlerMapping removes the need to set up the mapping between controllers and URLs explicitly in the web application context. Instead you define attributes in the controllers themselves, which contain information about the URL those need to be mapped to. Using the Commons Attributes compiler and indexer, Spring will be able to look up your controllers automatically. Using Commons Attributes to wire your controllers can be handy when you have simple controllers you need in more than just one web application and when you don't want to worry about mentioning them in the web application context.

Let's have a look at an example. First of all, we'll write ourselves a controller:

import org.springframework.web.servlet.mvc.ModelAndView; import org.springframework.web.servlet.mvc.AbstractController;     /**  * A simple controller preparing the model for a webpage  * welcoming the user and telling him the time.  *  * @@org.springframework.web.servlet.handler.metadata.PathMap("/showGenres.html")  */ public GenreController extends AbstractController {           private GenreDependency dependency;           public ModelAndView handleRequestInternal(HttpServletRequest request,     HttpServletResponse response)    throws Exception {         // create a model-and-view using 'welcome’ as      ModelAndView mav = new ModelAndView("genres");     // fill the model with the genres, retrieved using the dependency     // ...      return mav;   }       public void setDependency(GenreDependency dependency) {     this.dependency = dependency;   } }

This controller prepares a model containing genres and returns the ModelAndView accordingly. As you can see, we've included a Commons Attribute in the JavaDoc section of the controller mentioning a URL. At runtime, Spring will use this attribute to map the controller to the specified URL, in this case/showGenres.html. As with the other handler mapping based on URLs, you can use the Ant-style path expression here also.

We've also included a dependency the controller needs satisfied to be able to retrieve the list of genres. Controllers wired up using Commons Attributes or other metadata facilities available (such as JDK 1.5 metadata) don't need to be mentioned in the web application context; they're discovered by Spring based on the attributes. Because often you still need middle-tier services to retrieve information from, say, a database, Spring auto wires the controller when it discovers it. Auto wiring can be done using constructors and setters; the latter is shown in the previous example. More on auto wiring can be found in Chapter 9.

Important 

As you can see, we need to specify the fully qualified class name of the attribute (PathMap) in the JavaDoc attribute. We could also have imported the PathMap class, after which mentioning @@PathMap("/showGenres.html") would have sufficed. However, many IDEs detect unused imports and won't detect that in this case they may be used by a Commons Attributes Attribute. The remove unused imports features of those IDEs will remove the import statement mentioning the PathMap and compilation of the attributes using, for example, Ant will fail. We recommend using the fully qualified class name of the attribute you're using because errors that turn up after cleaning imports are hard to detect.

We've set up our controller but we still need to do two things. First of all, we need to mention the CommonsPathMapHandlerMapping in our context. This is pretty simple:

<bean />

The only thing left now is the compilation of the attributes using the Commons Attributes compiler and the indexing of the classes that contain attributes. This is explained in more detail in Chapter 9.

More Than One HandlerMapping

As a sophisticated IoC container, Spring allows you to define more than one instance of certain components. More specifically, this holds for HandlerMappings, ViewResolvers, and ExceptionResolvers (the latter two components are covered later in the chapter). In the case of handler mappings, the golden rule is that if a HandlerMapping does not deliver an appropriate HandlerExecutionChain (that is, it returns null), the next available HandlerMapping will be asked for one. Only if no appropriate result is found after inspecting all HandlerMappings will an exception be thrown.

If you define more than one handler mapping, Spring will inspect each one of them in the order resulting from sorting based on the Ordered interface. Customizing the order can be done by setting the order property on each of the handler mappings:

<bean >   <property name="order"><value>1</value></property> </bean>     <bean >   <property name="order"><value>2</value></property>   <property name="mappings">     <props>       <prop key="/list*.html">listShowsController</prop>       <prop key="/*">fallbackController</prop>     </props>   </property> </bean>

The example shows how to define two handler mappings where the BeanNameUrlHandlerMapping takes precedence over the SimpleUrlHandlerMapping. This causes any controller that is defined in the application context directly to be used before the controller mentioned in the properties mapping of the second HandlerMapping is inspected.



Professional Java Development with the Spring Framework
Professional Java Development with the Spring Framework
ISBN: 0764574833
EAN: 2147483647
Year: 2003
Pages: 188

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net