Submitted articles

Extending Spring @MVC

Author: Aleksa Vukotic
Company: Cake Solutions
e: aleksav@cakesolutions.net
blog: cakesolutions.net/teamblogs/category/aleksas-blog/

Spring framework has occupied a significant place in the hearts sand minds of web application developers since its inception in the form of the Spring MVC framework. As with anything else, Spring did not try to reinvent the wheel, but took the already established MVC pattern and provided an outstanding implementation of it, based around the Controller interface.

Spring 2.5 brought MVC to another level by giving us Spring @MVC, an annotation based framework for developing MVC web applications. The controllers became POJOs (plain old java objects), and all you needed to do to create a working controller to display a page in the browser was to add a couple of annotations.

Your controller implementation in Spring 2.5+ now looks like this:

@Controller
public class NewsController {
  @RequestMapping(value = “/news/index.html”, method = RequestMethod.GET)

  public String news(Model model) {

    model.put(content, “Big breaking news!”);

    return “news-template”;

  }

}

A Model instance is declared as a method argument, and is used within the controller method. This argument is entirely optional, so if you don’t want to use it in your handler method just create a method with no arguments. Spring supports the declaration of other types as arguments in the same way, notably HttpServletRequest and HttpServletResponse.

The return parameter of our method is a String that the Spring Framework then takes to represent a view name, which will be resolved to the View by the configured ViewResolvers. The actual ModelAndView instance will be created implicitly by Spring from the view and the model. However, you can also explicitly return a ModelAndView and save Spring all the hard work. Finally you can also return a void – this will tell Spring to create a ModelAndView instance using convention over configuration – by default the view name will be the value of the request mapping annotation. This is all very interesting, giving us the flexibility to write as little code as possible to implement functionality that we require but how can we extend this functionality in Spring, to use our own types as method arguments and return types.

Well, Spring does offer extension points to customise this behaviour, providing two interfaces: WebArgumentResolver (to resolve any custom Java type as controller method argument), and ModelAndViewResolver (to resolve any custom controller return type to ModelAndView instance). WebArgumentResolver.

The WebArgumentResolver interface has just one method:

Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) throws Exception;

The implementation should be able to create the required object type from the NativeWebRequest (Spring abstraction of the HttpServletRequest interface). It is the responsibility of the implementation to return a WebArgumentResolver.UNRESOLVED marker if it cannot resolve the given type.

Let’s say we want to resolve a Person type as a controller method argument. We would like to write code like this:

@RequestMapping(value = “/person/view.html”, method = RequestMethod.GET)

public String news(Person person, Model model) {

  model.put(“name”, person.getName());

  return “person-profile”;

}

Actually all we need to support it is to implement the WebArgumentResolver interface. Here is a sample implementation:

public class PersonWebArgumentResolver implements WebArgumentResolver {

  @Autowired PersonAccessService personAccessService;

 

  public ObjectresolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) throws Exception{

    if (Person.class == methodParameter.getParameterType()) { #1

     Long personId = Long.valueOf(webRequest.getParameter(“personId”)); #2

     return this.personAccessService.findPersonById(personId); #3

    }

    return UNRESOLVED; #4

    }

}

First we check the declared method parameter type (#1) – if it is the Person type, then proceed with extraction; if it’s not – return marker WebArgumentResolver.UNRESOLVED (#4). Since all WebArgumentResolver implementations are chained, returning UNRESOLVED tells Spring to continue the chain and try with a different implementation.

If we find that we are extracting Person type, we proceed with it, in our case by getting the “personId” parameter from the NativeWebRequest (#2). Finally, we load the Person object using our middle tier (possibly from the database), and return the object loaded (#3).

Note that we have annotated the class with @Component annotation in order for it to be discovered as a Spring managed bean. This allows it have access to all goodies of the Spring ApplicationContext, like dependency injection of the other beans. All we have to do now is plug our PersonWebArgumentResolver to the well-defined Spring extension point, the customArgumentResolvers property of the AnnotationMethodHandlerAdapter:

<bean class=”org.springframework.web.servlet.mvc.annotation. ↩

AnnotationMethodHandlerAdapter”>

  <property name=”customArgumentResolvers”>

    <list>

      <bean class=”net.cakesolutions.web.resolvers. PersonWebArgumentResolver”/>

    </list>

  </property>

</bean>

And that’s it! Now when you run your web application the Person object will be automatically extracted, and available in the handler methods.

ModelAndViewResolver

The other extension point of interest is having a custom return type for our controller methods, which can be extended by implementing ModelAndViewResolver interface. This interface has one method:

ModelAndView resolveModelAndView(Method handlerMethod,

    Class handlerType,

    Object returnValue,

    ExtendedModelMap implicitModel,

    NativeWebRequest webRequest);

Let’s extend out example, by having Person as return type for our controller method. We would like to be able to write code like this:

@RequestMapping(value = “/person/view.html”, method = RequestMethod.GET)

public Person news(Person person) {

  return person;

}

That would look swish, wouldn’t it – and all we have to do to have it is implement ModelAndViewResolver interface:

public class PersonModelAndViewResolver implements ModelAndViewResolver {

 

  public ModelAndView resolveModelAndView(Method handlerMethod,

          Class handlerType, Object returnValue,

          ExtendedModelMap implicitModel,

          NativeWebRequest webRequest) {

  if (returnValue instanceof Person) { #1

    Person person = (Person)returnValue;

      implicitModel.addAttribute(“name”, person.getName()); #2

      if (person.getType() == PersonType.Important) { #3

        return new ModelAndView(“person-important”, implicitModel);

      } else {

        return new ModelAndView(“person-normal”, implicitModel);

      }

    } return UNRESOLVED; #4

  }

}

Similar to the WebArgumentResolver, we need to check first if we can resolve the given return type by checking if it’s an instance of Person class (#1). Next, we want to make sure that the model passed to the view has the person name as an attribute (#2). Finally, depending of the “type” property of the returned Person object, we instantiate and return ModelAndView with the correct view name (#3).

In case our resolver cannot resolve given return type, the UNRESOLVED marker is returned (#4), informing Spring that we could not resolve the ModelAndView so any other ModelAndViewResolver can try. Lastly, we need to configure Spring’s AnnotationMethodHandlerAdapter to use our custom ModelAndViewResolver. No surprises there, as it’s done by setting customModelAndViewResolvers property on the AnnotationMethodHandlerAdapter:

<bean class=”org.springframework.web.servlet.mvc.annotation.

  AnnotationMethodHandlerAdapter”>

    <property name=”customArgumentResolvers”>

      <list>

        <bean class=”net.cakesolutions.web.resolvers. PersonWebArgumentResolver”/>

      </list>

    </property>

    <property name=”customModelAndViewResolvers”>

      <list>

        <bean class=”net.cakesolutions.web.resolvers. PersonModelAndViewResolver”/>

      </list>

    </property>

</bean>

And we can start using short-hand, easily readable code in our controllers by having a return type of Person resolved to a ModelAndView.

Summary

Since version 2.5 Spring has brought a lot of improvements to web application development with its annotation based controller configuration. Along with a lot of useful features, Spring now includes highly customizable extension points of which we have shown two in this article.

The WebArgumentResolver interface enables us to customize the way method arguments are injected into controller method calls. So we can easily just pass our custom objects as method arguments, and have them resolved from the incoming request in one place.

With the ModelAndViewResolver, we can have our own return types of the controller methods. The ModelAndViewResolver implementation will be then responsible to resolve the returned object to the correct ModelAndView. Along with selecting the view name, we can also add additional attributes to the model.

Sponsors

This web site, with its corresponding services, are provided because of the support of our sponsors who help in a number of vital ways. All the sponsors believe in the Open Source ethos and willingly offer their support. Please recognise this support by considering them when you require services in their field of expertise. Finally without the commitment from the community this would not be possible, so thank you to you all.

Founders

Cake Solutions

OpenCredo

Skills Matter - Open Source and Agile Training & Events

Sponsors

FDM Group

Hays - Recruiting experts worldwide