Karthik's Weblog

My love hate relationship with Spring MVC’s AnnotationMethodHandlerAdapter.

Posted in Spring by karthik on December 16, 2009

I like the Spring 3.0 annotation based controllers. I never understood how Spring “MVC” managed to sustain its popularity all this while with a programming model based on “controller inheritance” especially when you had competing frameworks like Struts2 and Stripes that supported a pure POJO controller model from day one.
So it was heartening to know that Spring 3.0 will deprecate the Inheritance based controllers going forward.

The class that does the majority of the work in Spring @MVC is AnnotationMethodHandlerAdapter (with the help of a few others).

One of the nice features supported by Spring MVC is the ability to selectively turn-off automatic data binding from request parameters to model objects. You usually specify the fields that you don’t want bound in the @InitBinder method.

In this example, field Foo.id is auto-generated and say I don’t want to auto-bind the field “id”, you usually specify that information in your @InitBinder method in the @Controller.

class Foo{
private String name;
private int id;
private String
//getter-s
//setter-s
}

While this is enough to prevent auto-binding,
you might still want to signal this security breach as an error in your controller method.

@Controller
class FooBarController{
@InitBinder
public void myInitBinder(WebDataBinder binder){
//do not bind the field id
binder.setDisallowedFields(new String[]{"id"});
}
@RequestMapping("/foo")
void foo(Foo foo, BindResult bindResult){
String[] suppressedFields = result.getSuppressedFields(); #1
if (suppressedFields.length > 0) { #2
throw new IllegalBindOperationException( #3
"You've attempted to bind fields that aren't allowed by @InitBinder: "
+ StringUtils.arrayToCommaDelimitedString(suppressedFields)); #4
}
//do something useful }
@RequestMapping("/bar")
void bar(Foo foo, BindResult bindResult){
String[] suppressedFields = result.getSuppressedFields(); #1
if (suppressedFields.length > 0) { #2
throw new IllegalBindOperationException( #3
"You've attempted to bind fields that aren't allowed by @InitBinder: "
+ StringUtils.arrayToCommaDelimitedString(suppressedFields)); #4
}
//do something useful }
}

You obviously don’t want to be writing this piece of logic (lines #1 through #4) in every handler method.
May be you can move this check to a utility API and call the utility API instead. But then Spring encourages us to model such concerns as an interceptor.

But how do I access the BindResult object in the interceptor unless the handler method declares it to be a part of its signature? While I’m not a fan of AspectJ, do let me know if there is a way.

Another option could be to achieve this by subclassing AnnotationMethodHandlerAdapter and overriding some relevant methods? But which one? – I couldn’t find any – let me know if you did.

Ok how about HandlerMethodInvoker? anybody?

Finally I realized that while AnnotationMethodHandlerAdapter & HandlerMethodInvoker combine to give us this excellent @MVC programming model, these classes like to keep everything to themselves – pretty much every interesting method /inner class in some cases is private.

Suffice to say that it’s been quite frustrating.

I was hoping for a “redesigned” AnnotationMethodHandlerAdapter class for Spring 3.0. But that hasn’t happened either.
Let me point out at the same time that AnnotationMethodHandlerAdapter is customizable but I feel it could be much better.
I love it but I hate it too.

Advertisements

One Response

Subscribe to comments with RSS.

  1. Aaron said, on December 16, 2010 at 10:09 am

    Not sure if its going to help, but here is a good example of using the Interceptor model with the AnnotationMethodHandlerAdapter to intercept the request and modify it:
    http://www.gridshore.nl/2010/11/28/playing-with-the-spring-mobile-project/


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: