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

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:
- 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.).
- The centralized approach (SimpleUrlHandlerMapping). A central place to see URL patterns and controller mappings.
- 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
- Content Negotiation using Spring MVC
- Spring 3.1 M2: Spring MVC Enhancements
- Annotated Web MVC Controllers in Spring 2.5
- Ajax Simplifications in Spring 3.0
- Spring MVC 3.2 Preview: Chat Sample





Adam Sherman says:
Added on March 24th, 2008 at 10:38 amI really like this approach. However, how do we replicate the existing functionality in the abstract controller classes? (AbstractCommandController, etc.)
A.
Rossen Stoyanchev (blog author) says:
Added on March 24th, 2008 at 12:46 pmAdam, 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.
tzolov says:
Added on March 24th, 2008 at 6:44 pmHi 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
Juergen Hoeller (blog author) says:
Added on March 27th, 2008 at 5:53 amFYI, 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
Javier Diaz says:
Added on September 19th, 2008 at 9:52 amHi.
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
Javier Diaz says:
Added on September 19th, 2008 at 10:11 amSorry the xml part was missing
[quote post="297"]
/ltb/*.cgi = ltbController
[/quote]
Javier Diaz says:
Added on September 19th, 2008 at 10:44 amSorry, 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>
Rossen Stoyanchev (blog author) says:
Added on September 19th, 2008 at 12:31 pmJavier,
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
miguel says:
Added on March 26th, 2009 at 3:14 pmThis 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?
miguel says:
Added on March 31st, 2009 at 6:03 pmMy 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?
Rossen Stoyanchev (blog author) says:
Added on March 31st, 2009 at 8:24 pmHi 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.
John Smith says:
Added on January 3rd, 2010 at 2:29 pmHi:
Does anybody know where to get the full code download for this excellent example?
John Smith says:
Added on January 3rd, 2010 at 2:36 pmHi:
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.
Rossen Stoyanchev (blog author) says:
Added on January 4th, 2010 at 4:09 amJohn, 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.
rahul says:
Added on January 25th, 2010 at 9:45 amHi,
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
Eric S says:
Added on June 4th, 2012 at 2:40 pmI really like this hybrid approach…however it seems it is not supported in Spring 3.1. Can you show the Spring 3.1 equivalent of this? (Or is Spring 3.1 actively discouraging a hybrid approach?)
Rossen Stoyanchev (blog author) says:
Added on June 5th, 2012 at 9:04 amEric, the hybrid approach is attractive for mapping controllers in a central place and using method-level annotations to narrow down the choice within the controller.
The approach however leads to strange scenarios when you're not mapping controllers centrally. Especially when using all annotations, the split-decision process becomes even more problematic. I covered this topic in some detail in this presentation. It has links to a couple of representative issues but there are many more.
Taking a step back, is what's attractive about the hybrid approach the fact that it provides a central place to specify mappings? If you look at the RequestMappingHandlerMapping you will see the protected method getMappingForMethod(Method, Class< ?>). It returns a RequestMappingInfo, which can be prepared from anything — currently from annotations only but it could be any other source. There is a ticket to that extent that you can track or comment on.
Eric S says:
Added on June 5th, 2012 at 2:41 pm> Taking a step back, is what's attractive about the hybrid approach the fact that it provides a central place to specify mappings?
Yes, exactly!
Frankly it's mind boggling to me that Spring doesn't have a good solution for this very basic and important problem, after so many versions that continually offer (and push users toward) new ways of configuring things, and even pushing us in a direction that makes this problem worse. Rails has routing tables, why hasn't Spring learned from that already? Why isn't such a thing the default rather than something you can only accomplish by digging deeper into spring to customize it?
(BTW, your presentation link gets me to a single slide with no apparent way of navigating to other slides…)
Anyway, thanks for the reply and sorry if I sound ranty.
Rossen Stoyanchev (blog author) says:
Added on June 5th, 2012 at 3:43 pmThere are pros and cons for each approach and I do believe annotations are serving a very good purpose. It's also relatively easy to produce a central listing of all request mappings while keeping the configuration close to the code, see [1], [2], [3].
As for the presentation, just click on the slides and use the arrow keys to navigate. This is quite common with online presentations.
[1] swagger-springmvc
[2] Generating WADL for Spring MVC
[3] EndpointController.java
Eric S says:
Added on June 5th, 2012 at 6:43 pmClicking the slides doesn't work. I did eventually stumble on the arrow keys working, but one does expect clicking to work.
Being able to generate documentation of them is one thing, but it is not nearly as good as being able to maintain all of your mappings in one spot.
I understand that different approaches have pros and cons, but it is frustrating to me that when Spring changes its latest favored approach they push it, selling all its pros with no mention of its cons. I can't help but feel that some of these changes are driven by software fads. Currently annotations are "in" and XML configuration is "out". Surely a way could have designed that offered these same pros without the cons. Or, if there is no way of offering a particular pro without a particular con, offer both ways as equally supported.
Meanwhile every change Spring makes has a cost in littering the web and existing projects with complexity. Finding an example of how to do something or solve a particular problem becomes harder and harder, as any given explanation or example you find on the web may nor may not be compatible with the given project you are working on (which may have been implemented by someone else with who knows what flavor of spring's best new way at the time).
(My experience encountering this blog post is a perfect example of this scenario.) Again, sorry to rant. My purpose is not to bash Spring. But I would like the Spring developers to understand these downsides and costs and perhaps consider them more seriously in the future.
Rossen Stoyanchev (blog author) says:
Added on June 6th, 2012 at 7:04 amYou would be hard-pressed to find a project with a stronger record of maintaining backwards compatibility than Spring. Seriously!
As much as you would like to see it as annotations vs XML because one is the latest fashion, it's really not that simple. The kind of fine-grained mappings annotations allowed was never possible with XML where you could only map a URL to a class and the class typically had to implement an interface. So this is very much an evolution path and the kind of route mappings you mention would be yet another evolution path, it wouldn't be XML based configuration.
If you like the approach in this post then you can still use the DefaultAnnotationHandlerMapping and the AnnotationMethodHandlerAdapter. They'll give you the exact same behavior in 3.1.
My last point is that Spring is an open-source project and ranting is pointless. We have JIRA where comments and voting takes place, a process for contributions if you'd like to change something, or you can develop your own like this springmvc-router project.
Alex Nunez says:
Added on July 18th, 2012 at 7:29 pmHi Rossen,
Your aproach is my preferred one, although I ran into a problem I got stuck with.
This approach doesnt allow me to declare 2 times the same mapping handler in different controllers. It only works if you map the controller with an annotation.
Any ideas?
Please see:
http://stackoverflow.com/questions/11255706/spring-mvc-3-same-request-mapping-different-controller-mapping
Thx!