Wednesday 12 June 2013

Leveraging the Spring MVC 3.1 HandlerMethodArgumentResolver interface

 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 button to 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 HandlerMethodArgumentResolver can be used to remove such framework limits. The result is that your business needs are again in control, not the technology.

Background

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 @Controller bean. With @RequestMapping you can specify the URL being mapped and the HTTP method that a controller method understands. You can also use the annotations @PathVariable and @RequestParam to extract URL templates and request parameters from requests, respectively. The pièce de résistance for REST are the annotations @RequestBody and @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 HandlerMethodInvoker (used in AnnotationMethodHandlerAdapter). Not the prettiest solution, and nearly impossible to extend. You can augment some functionality by using AOP, but nothing more.

Spring 3.1

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 @RequestMapping annotated methods.

The class RequestMappingHandlerAdapter checks all method parameters against its lists of custom and default HandlerMethodArgumentResolver instances, 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 pom.xml:

And finally, we will need to adjust the Spring version of our dependencies to: 3.1.0.BUILD-SNAPSHOT


No comments: