A familiar problem when implementing a REST server, is that your framework limits what you can do. Sometimes, this problem can be mitigated (not solved) by reformulating (i.e. changing) your business needs. For example, you can reformulate
save multiple changes to an invoice and its invoice lines with one click on a save buttonto
save each of multiple changes to an invoice and its invoice lines as separate UI actions. Unfortunately, this change increases your audit trail and thus technology trumps business needs.
Another limit is that conversions have no context. This makes the conversions easier, but also makes functionality like partial updates impossible. That is, it is not possible to that a request body can contain any subset of the changeable properties of a resource, and that all properties that are left out are left at their current values. The default Spring implementation, for example, treats undefined properties as
set to their default value. Our goal was to change that to
keep the current value in the database.
In this in-depth technical article, I will show you how the new Spring 3.1 interface
HandlerMethodArgumentResolvercan be used to remove such framework limits. The result is that your business needs are again in control, not the technology.
Spring MVC 3.0 supports REST using the annotation
@RequestMapping. This annotation is also used to map normal web application requests to a method of an
@RequestMappingyou can specify the URL being mapped and the HTTP method that a controller method understands. You can also use the annotations
@RequestParamto extract URL templates and request parameters from requests, respectively. The pièce de résistance for REST are the annotations
@ResponseBody, which map Java classes from the request body, respectively to the response body.
If you look at the code, you will see that the translation from the request to the invocation of the controller method is done using a big if/else statement in the class
AnnotationMethodHandlerAdapter). Not the prettiest solution, and nearly impossible to extend. You can augment some functionality by using AOP, but nothing more.
Spring 3.1 will improve this by using a new class,
RequestMappingHandlerAdapter, which implements a strategy pattern using the interface
HandlerMethodArgumentResolver. The old/current implementation,
AnnotationMethodHandlerAdapter, will remain for backward compatibility.
The core of the new implementation (at least for developers) is the interface
HandlerMethodArgumentResolver. This interface has two methods:
supportsParameter(MethodParameter)to check if a method parameter is supported, and
resolveArgument(MethodParameter, ModelAndViewContainer, NativeWebRequest, WebDataBinderFactory)to actually resolve the method parameter from the request. Default implementations exist for all supported parameters of
RequestMappingHandlerAdapterchecks all method parameters against its lists of custom and default
HandlerMethodArgumentResolverinstances, and the first one that says it supports the method parameter will be used to resolve it.
This new interface gives us some very nice opportunities. I will explain some of them by demonstrating how easy it is to implement your own
HandlerMethodArgumentResolver. The examples will show how to extend the REST framework provided by Spring, including adjusting the behavior of the existing Spring implementations.
The Spring version we are using is the latest and greatest, so first we need to add the Spring snapshot repository to our
<name>Spring Maven Snapshot Repository</name>
And finally, we will need to adjust the Spring version of our dependencies to: