Oliver Gierke Archive About Tags

Neglected classes in Spring - Part II

24 January 2010

If you have worked with Spring 2.5 annotation based web MVC framework you know that it allows you to freely design you controller method signatures:

@RequestMapping(value = "users", method = GET)
public String showUsers(Model model) {
  model.addAttribute("users",
    userManagement.getUsers());
  return "users";
}

As you can see we use a parameter of type Model that will we automatically get an instance handed by Spring. For a complete list of supported parameter and return types see Spring reference documentation on that topic.

But why do I mention that? The title says “neglected classes”, not “things I also could have read up in the reference docs”. The crucial thing here is to know that the list of supported types is not fixed and there is an SPI to write your class allowing to get a parameter injected automatically.

Pagination

The first example I want to come up with is pagination. The OpenSource framework Hades provides dedicated support to allow paginated access to databases via JPA. The core abstractions achieving this is the Pageable interface in combination with the PageRequest implementation class. But there is still the question on how to create instances of PageRequest from an incoming request. A naive might be the following:

@RequestMapping(value = "/users", method = GET)
public String showUsers(Model model, WebRequest request) {

  Integer size = // extract size from request
  Integer page = // extract page from request
  Sort sort = // extract sort options from request

  Pageable pageable =
    new PageRequest(page, size, sort);

  model.addAttribute("users",
    userManagement.getUsers(pageable));
  return "users";
}

What’s wrong with this? First, it shouldn’t be the controllers task to extract pagination information from the request. Second, even if we’d move this code into a utility class, we’d be forced to get WebRequest injected into every controller method that wants to use pagination, which breaks the abstraction level gained.

So what about rather something like this:

@RequestMapping(value = "/users", method = GET)
public String showUsers(Model model,
  Pageable pageable) {
  model.addAttribute("users",
    userManagement.getUsers(pageable));
  return "users";
}

public class PageableArgumentResolver implements WebArgumentResolver {

  @Override
  public Object resolveArgument(
    MethodParameter methodParameter,
    NativeWebRequest webRequest) throws Exception {

    // implement extraction logic here
  }
}

Inside the implementation your first task is to determine, whether you can return the given MethodParameter or return a WebArgumentResolver.UNRESOLVED. Our resolver would have to check if the target type is a Pageable and extract the request parameters then.

Accessing the current user

A very often faced problem in web controllers is that one actually has to know which user has triggered the request. This could be of course done programatically inside the controller method similar to the naive approach shown above. What about this approach:

@RequestMapping(value = "/myaccount", method = GET)
public String showUsers(Model model,
  @CurrentUser UserDetails userDetails) {
  model.addAttribute("user",
    userManagement.getUser(userDetails.getId());
  return "user";
}

Similar to the plain type based WebArgumentResolver implementation for Pageable the implementation here would check for the custom CurrentUser annotation and then access e.g. Spring Security API to access the currently logged in user. The additional annotation is “necessary” in this case, as one could also have a UserDetails used as ModelAttribute, but that depends on the detailed requirements of your application.

Summary

WebArgumentResolver allows providing your own custom controller method parameters to centralize either lookup or extraction logic, that would otherwise be scattered through your controller code.

blog comments powered by Disqus
Fork me on GitHub