Using a Hybrid Annotations & XML Approach for Request Mapping in Spring MVC

Rossen Stoyanchev

In Spring 2.5 it is possible to use annotations to configure all parts of a web application. Seeing annotations applied is particularly interesting in the Web layer where developers traditionally rely on the SimpleFormController and the MultiActionController for form page handling. The introduction of annotations has created a third option, one that does not require a base class while still offering the flexibility of previous approaches.

While it is easy to see the elegance in using annotated POJOs to implement Controllers, the benefit is not as clear in the area of URL-to-Controller mappings. What would it be like to define all your URL mapping rules using annotations? Indeed this is one area in which centralized configuration has worked well for developers of Spring MVC applications.

Lets review the Spring 2.0 options for URL-to-Controller mappings:

  1. The bean name approach (BeanNameUrlHandlerMapping). The name of each bean holds the path it serves. Despite its simplicity, this approach can scale if combined with coarse-grained servlet mappings (e.g. "/browse/*", "/order/*", "/reports/*", etc.).
  2. The centralized approach (SimpleUrlHandlerMapping). A central place to see URL patterns and controller mappings.
  3. The convention over configuration approach (ClassNameUrlHandlerMapping). Matches URL paths to class names. Hence "/accounts/*" is mapped to a MultiActionController of type AccountsController. No explicit mappings are required.

Spring 2.5 adds a fourth option in the form of the @RequestMapping annotation, which can be placed on a class or a method. When placed on both the method-level mapping narrows the class-level mapping.

Here is a SimpleFormController-style workflow in which the method-level mapping is narrowed down by request method:

@Controller
@RequestMapping("/editAccount")
public class EditAccountController {

    @RequestMapping(method=RequestMethod.GET)
    public Account setupForm(@RequestParam("id") Long id) {
        …
        return account;
    }

    @RequestMapping(method=RequestMethod.POST)
    public String processSubmit(Account account) {
        …
        return "redirect:/success.htm";
    }
}

And here is a MultiActionController-style delegation in which the method-level mapping is narrowed down by both request method and relative path:

@Controller
@RequestMapping("/accounts/*")
public class AccountsController {

    @RequestMapping(method=RequestMethod.GET)
    public List<Account> list() {}

    @RequestMapping(method=RequestMethod.GET)
    public Account show(@RequestParam("id") Long id) {}

    @RequestMapping(method=RequestMethod.POST)
    public String create(Account account) {}
    …
}

As you can see, with the "/accounts/*" mapping embedded in the code it can be difficult to enforce an application-wide convention for Controller mapping. At least not without some rigorous discipline. Fortunately there is a way to combine an external HandlerMapping with method-level @RequestMapping annotations. Below is an example illustrating how this approach works:

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <value>
            /accounts/*=accountsController
        </value>
    <property>
</bean>
@Controller
public class AccountsController {

    @RequestMapping(method=RequestMethod.GET)
    public List<Account> list() {}

    @RequestMapping(method=RequestMethod.GET)
    public Account show(@RequestParam("id") Long id) {}

    @RequestMapping(method=RequestMethod.POST)
    public String create(Account account) {}
    …
}

Here the controller mapping resides in a central XML-based mapping while the action method mapping is specified through annotations. This approach can be described as a POJO MultiActionController with annotation-based method dispatching. In fact in a recent communication within SpringSource, Juergen pointed out that providing an annotation-based alternative to the MultiActionController was an explicit Spring 2.5 design goal so I guess there are no surprises there! Furthermore, there is work under way to allow you to combine method-level @RequestMapping annotations with the ControllerClassNameHandlerMapping convention (see SPR-4129).

So what's the significance of all this?

An all XML-based approach to configuring the Web layer can get noisy, but centralized, externalized configuration does have its place. Extending from framework-specific base classes with deep inheritance hierarchies to implement control logic can also get noisy, and we generally believe that should be avoided if you can help it. In Spring MVC 2.5, annotations can help address both of these concerns by encapsulating method mapping rules inside your Controller class and also allowing you to implement your Controllers as POJOs. Furthermore, the hybrid approach above shows how you can get the best of externalized configuration and annotation-based configuration.

Similar Posts

Share this Post
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • DZone
  • LinkedIn
  • Slashdot
  • Technorati
  • TwitThis
 

15 responses


  1. I really like this approach. However, how do we replicate the existing functionality in the abstract controller classes? (AbstractCommandController, etc.)

    A.


  2. Adam, a few different annotations can help gain much of the same functionality. For example during a form submit you can get access to the command object populated with a DataBinder by placing the @ModelAttribute("command") annotation on an input parameter. The @SessionAttribute("command") on a class lets you store a model attribute in the session between requests. The @InitBinder annotation placed on a method lets you customize the DataBinder. Take a look at the Petclinic sample in Spring 2.5, which has been updated to use Spring MVC annotations. All of these are shown there.


  3. Hi Rosene,
    While mixing the traditional Controller sub-class style with the annotation based style I’ve noticed that my traditional controllers were not mapped until I've defied the SimpleControllerHandlerAdapter explicitly in the application context.
    So if the AnnotationMethodHandlerAdapter is defined explicitly then all default adapters are implicitly turned off and the traditional (old style) controllers do not work. To solve it I had to define the default handler SimpleControllerHandlerAdapter explicitly.

    Cheers, Chris


  4. FYI, the ControllerClassNameHandlerMapping revision for @Controller beans is now available in the latest 2.5.3 snapshots! It autodetects @Controller beans as well now (when running on Java 1.5 or above), mapping the according to the class name strategy.

    Juergen


  5. Hi.

    I'm trying to use this approach. I really like how things get organized if you follow this approach.

    Some of our controllers (all of then annotated with @Controller) have parameters being passed in the constructor. I'm trying not to use @Autowired as Spring IDE is still not supporting it (as far as I know)

    So I'm defining the bean for the controller with the constructor-arg, which works ok, but of course it looks a bit silly to me as I'm defining the same bean twice. See below:

    /ltb/*.cgi = ltbController

    What I would like to know is what would be the best way to implement the approach you described in this blog if you need inject parameters to your constructor and you don't want to use @Autowired?

    Thx
    Javier


  6. Sorry the xml part was missing

    [quote post="297"]

    /ltb/*.cgi = ltbController

    [/quote]


  7. Sorry, here again
    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping" >
    <property name="mappings">
    <value>
    /ltb/*.cgi = ltbController
    </value>
    </property>
    </bean>

    <bean id="ltbController" class="com.lastminute.lfe.dashboard.web.LtbController">
    <constructor-arg ref="jdbcSubagentManager"/>
    </bean>


  8. Javier,

    There is an SPR mentioned in the posting that was resolved in Spring 2.5.3. Have a look at using ControllerClassNameHandlerMapping instead of SimpleUrlHandlerMapping. The approach works with both @Controller and with XML-based controllers.

    For more details see this infoq article:
    http://www.infoq.com/articles/spring-2.5-ii-spring-mvc


  9. This approach alleviates one of my concerns with the @Controller technique. But my other main concern is how I have lost the ability to have base controllers process common portions of request, for example for security. To me, having a base controller is much more readable and understandable than having filters or interceptors doing magical invisible things that some developers might not notice, it's also more OO-ish.

    I suppose one approach would be to create methods in a base controller tagged with @RequestMapping for GET and POST like this:

    public abstract class BaseSecurityController {
    @RequestMapping(method=RequestMethod.GET)
    public String checkSecurity(HttpServletRequest req) {
    if (!securityService.checkAuth(req)) {
    return "loginView";
    }
    handleGet(req);
    }
    public abstract void handleGet(HttpServletRequest req);
    }

    @Controller
    public class SomePageController extends BaseSecureController {
    @Override
    public String handleGet(HttpServletRequest req) {
    //….
    return "view";
    }
    }

    But that removes a lot of the flexibility of the method signatures and @RequestMapping. Is there a better technique?


  10. My comment has not received any responses, maybe because this blog post is old, maybe because I didn't explain myself well or maybe because I provided a bad example since Acegi is available for security. Still, I think there is a fatal flaw in @MVC which I haven't seen addressed anywhere and it worries me because apparently the interface-based MVC is going to be deprecated.

    Maybe a better example about my concern is a web site with a common HTML component across all pages, perhaps a menu or a dynamic header. Using @MVC, every single @RequestMapping method will have to populate the model with the data for the common components. You could extract this into some external method, but there is no way around the fact that every single @RequestMapping method will have to make this call.

    Does no one else see the inability to do OO inheritance as a problem? To me, the loss of inheritance is a hell of a price to pay just to simplify XML configuration. Inheritance is one of the main features of an OO language, without OO is this even Java anymore?


  11. Hi Miguel, have you thought about using a HandlerInterceptor? The security example you provided does not seem to be in any way tied to the controller.


  12. Hi:

    Does anybody know where to get the full code download for this excellent example?


  13. Hi:

    I downloaded "spring-framework-2.5.6.SEC01" but I can't seemed to find an example of a Spring Mvc Annotations example in the "C:\Download\Spring25\temp\spring-framework-2.5.6.SEC01\samples" directory. Does anybody know of a good Spring MVC Annotations example I can download from the web or from the spring web site.

    Any help or hint would be greatly appreciated it.

    Yours,

    Frustrated.


  14. John, you will find sample code associated with this blog entry (http://blog.springsource.com/2009/12/21/mvc-simplifications-in-spring-3-0/) based on Spring 3.0 that is now final. More quality samples available at http://springbyexample.org. Last but not least I recommend you give Roo a try http://blog.springsource.com/2009/12/31/spring-roo-1-0-0-released/. It will generate you a Spring MVC application in just a few commands.


  15. Hi,
    I have one problem facing using the Spring MVC using annotation.
    Can you please look into it.. As i am new to it. Because Form controller become deprecated. So , i am going for Annotation.

    Please see the post which i have done in the spring web forum.
    As per me , it may be bug inside the spring 3.0…..

    http://forum.springsource.org/showthread.php?t=83605

    if you are not able to reach this post , then search for "RequestMaping calling two times for one submit"

    Please reply back

    Thanks
    Rahul

2 trackbacks

Leave a Reply