Trifork Blog

Simple forms with Spring MVC 2.5

May 14th, 2009 by
| Reply

Having used the Spring MVC 2.5 webframework for a while now, I felt the urge to blog about some of the experiences I had with it. Especially about the part concerning forms, validation, binding and property editors. That’s the part that was usually provided to you by the SimpleFormController (in the ‘old’ Spring MVC). But now, in Spring MVC 2.5, where do you start? You don’t have a convenient base class anymore that tells you what methods you can override. Instead you have a bunch of annotations and a lot of magic.

Just to clarify, I love Spring MVC 2.5. It provides much more freedom and reduces the amount of controller classes you need to write, compared to previous versions of the framework. But you need to follow some best practices to avoid making a mess of your controller classes.

Return types

With your first glance at Spring MVC 2.5 you have probably noticed that controller methods don’t have to return a ModelAndView anymore. Instead, controllers are allowed to return nothing (void), a String representing a view, or just a model. This creates a lot of freedom, but in my opinion can also cause some inconsistency and unclarity. By convention, if your controller does not explicitly return a view, the view name is defined by converting the mapped URL into a view name. This will save you some code, but you have to remember this convention to know what’s going on.

You should be careful not to mix the return types of all your controller methods. I suggest picking a return type and sticking with it. In my case I picked the old fashioned ModelAndView since it can contain both a view and a model, which works in all cases. This makes your controller methods consistent and makes it more clear what to expect from those methods.

Forms & binding

To create a very basic form you basically only need to handle a request to view the form and to submit the form. Let’s say we have the following class:

public class User {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Instances of this class will get manipulated by the form controller we will create. The SimpleFormController used to call an instance of this class the “form backing object”. Let’s keep calling it this way. Implementing this very basic form in Spring MVC 2.5 would typically look like this:

@Controller
@RequestMapping(“/userForm.html”)
public class UserFormController {

    @RequestMapping(RequestMethod.GET)
    public ModelAndView show() {
        return new ModelAndView(“userForm”, “user”, new User());
    }

    @RequestMapping(RequestMethod.POST)
    public ModelAndView submit(User user) {
        // Do something with the submitted User
        return new ModelAndView(new RedirectView(“/userForm.html”));
    }
}

Notice that you don’t need to specify a command class. Instead, you only have to specify the class as an argument in the submit method and request binding will automagically occur.

Custom form backing object

In a lot of cases you will want to create and initialize the form backing object manually. In the previous example a new instance of User is automatically created when the submit occurs. With the old SimpleFormController, creating your own form backing object could be done by overriding the formBackingObject() method. In Spring MVC 2.5, you can achieve the same behavior by specifying the @ModelAttribute annotation on the method responsible for creating that object.

@ModelAttribute(“user”)
public User formBackingObject() {
    return new User();
}

This method can also specify all kinds of arguments just like a normal controller handler method. So you could for example do the following:

@ModelAttribute(“user”)
public User formBackingObject(Long id) {
    if (id != null) {
        return userDao.findById(id);
    }
    return new User();
}

This makes it possible to use the same form controller for both adding and editing existing instances of the User object. Having a @ModelAttribute annotated method in your controller class basically tells Spring MVC to include whatever is returned by that method to every request handled within that controller class.

To make sure your submit method uses the same instance, you have to annotate its argument with @ModelAttribute as well.

@RequestMapping(method = RequestMethod.POST)
public ModelAndView submit(@ModelAttribute(“user”) User user) {
    // Do something with the submitted User
    return new ModelAndView(new RedirectView(“/userForm.html”));
}

When a submit request comes in, first our formBackingObject() method is called. Second, request binding occurs on this object. And finally this object is passed to the submit() method. So the object passed to submit() is the same instance as the one created by formBackingObject().

Handling bind errors

By default, Spring MVC 2.5 throws an exception when errors occur during request binding. You usually don’t want that. Instead you want to handle those errors yourself by presenting these errors to the user.

To do this, you have to define a BindingResult argument in the submit() method. This argument has to be right after the object on which request parameters are bound to.

public ModelAndView submit(@ModelAttribute(“user”) User user, BindingResult bindingResult)

For the exceptional case that you are binding to multiple objects, you can specify a BindingResult for each one of them like this:

public ModelAndView submit(User user, BindingResult userBindingResult,
Credentials credentials, BindingResult credentialsBindingResult)

So now what happens is that no exception is thrown when bind errors occur. Instead, errors are registered on the BindingResult that is passed to your submit method. You can now decide what to do with the errors. In most cases you only want to cancel the submit and return to the form view which displays the errors to the user. A simple if statement can do the trick.

@RequestMapping(method = RequestMethod.POST)
public ModelAndView submit(@ModelAttribute(“user”) User user, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        return new ModelAndView(“formView”);
    }
    // Do something with the submitted User
    return new ModelAndView(new RedirectView(“/userForm.html”));
}

To display the errors in your JSP view (in this case called “formView”) you can simply use the Spring form taglib like you did using the old Spring MVC.

Validation

With SimpleFormController the only thing you needed to do for validation is write your own Validator class and inject it into the SimpleFormController bean. With Spring MVC 2.5 you can still use that same Validator class and inject it into your controller, but now you have to call it manually.

@RequestMapping(method = RequestMethod.POST)
public ModelAndView submit(@ModelAttribute(“user”) User user, BindingResult bindingResult) {
    userValidator.validate(user, bindingResult);
    if (bindingResult.hasErrors()) {
        return new ModelAndView(“formView”);
    }
    // Do something with the submitted User
    return new ModelAndView(new RedirectView(“/userForm.html”));
}

You pass your validator the same BindingResult that holds the binding errors. The validator should register any errors on this object as well.

Property editors

Finally, I want to explain how property editors work in Spring MVC 2.5. Property editors are used by the request binder to help converting String values to other types (like a String to a Date or to a User instance). Actually, nothing has changed here except you have to specify the @InitBinder annotation instead of overriding the initBinder() method on SimpleFormController.

@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(new CustomDateEditor(“dd/MM/yyyy”, true));
    // Register any property editor you want
}

The property editors you register here are used for all binding operations performed within the same controller class.

Conclusion

Spring MVC 2.5 provides an elegant approach to handle form related requests, but you just have to know how exactly.

I hope you found this information useful. Please leave a comment in case you have questions about this topic, or want to share your experiences with Spring MVC.

26 Responses

  1. May 14, 2009 at 22:34 by Alef Arendsen

    Nice to see some love for Spring MVC once every now and then :-)!

  2. May 15, 2009 at 13:37 by Al

    How do you unit test your submit method if you have to include a BindingResult in its signature?

  3. May 15, 2009 at 16:56 by David Karr

    Very useful. However, I would appreciate a little more clarification on the details of a @ModelAttribute-annotated method. I've reviewed all the relevant documentation, and a few books, and they all "wave their hands" a bit when discussing this. I get that these methods are called before a @RequestMapping-annotated method, but now your example presents a wrinkle that I haven't seen before. Every @ModelAttribute-annotated method I've seen before didn't take arguments. Yours does. If this method is called, how does it know what parameter value to send, and where does it get it from?

    It seems odd to me that @ModelAttribute is overloaded for two purposes, on methods and on parameters. It seems to me that the two usages are quite semantically different. Using the same name adds a bit to the confusion.

  4. May 15, 2009 at 21:42 by David Karr

    It appears my confusion is due to the following excerpt from your article:

    ------------
    @ModelAttribute(“user”)
    public User formBackingObject(Long id) {
    if (id != null) {
    return userDao.findById(id);
    }
    return new User();
    }
    ------------

    Which I (and Mark Fisher, a member of the Spring team) believe should instead be:

    ------------------
    @ModelAttribute(“user”)
    public User formBackingObject(@RequestParam("id") Long id) {
    if (id != null) {
    return userDao.findById(id);
    }
    return new User();
    }
    ------------

    This change, along with some other documentation I've found, clarifies my understanding.

  5. May 15, 2009 at 22:55 by Stevo Slavic

    Well done, nice sum up!

    What's the best practice to handle situation where one has Hibernate implementation of userDAO, and a user is being updated with invalid data, formBackingObject gets called before submit, it obtains user from hibernate session, then automatic binding of submitted form data to fetched object occurs with no errors, on submit validation fails and populates errors, because there are errors a form view is returned, but along with all this an unwanted save to the db of submitted modified data will occur.

  6. May 17, 2009 at 18:46 by Tomislav

    Finally, I was looking for this for awhile.

  7. May 18, 2009 at 12:41 by Tom van Zummeren

    @Al: Since BindingResult is an interface, you can easily mock/stub it using a framework like Easymock or Mockito or make your own "DummyBindingResult" subclass. This mock/stub implementation can be passed to the tested controller method.

  8. May 18, 2009 at 12:56 by Tom van Zummeren

    @David Karr: A @ModelAttribute method can accept arguments just like a @RequestMapping method. So arguments can be passed like HttpSession, HttpResponse etc, but also arguments like String, Integer, Date etcetera. In the latter case the values for these arguments are retrieved from the current request parameters. The fact that this is possible is not documented (as far as I know) but can be quite useful. I disagree that its confusing that @ModelAttribute can be used for both parameters and methods, because the end result stays the same: an object is bound to the model. It doesn't matter if this value was calculated by a method, or specifed by a method argument, the behavior is similar.

    And to reply to your second comment: If adding the @RequestParam annotation clarifies your understanding you should add it. But the default behavior of Spring MVC 2.5 is that if an argument has no annotation it is retrieved from the request as an optional argument (can be null). I like this because it reduces code and it is still clear to me what's happening.

  9. May 18, 2009 at 13:11 by Tom van Zummeren

    @Stevo Slavic: If I understand you correctly, you are worried about changes made to the attached hibernate object are persisted into the database when validation errors occur. Whether or not this happens, depends on how you configured your transaction boundaries. I think it is a bad practice to have a transaction configured on http request level. If you do that then indeed you are right, updates are immediately persisted to the database, while in error. The best practice is to create a service class and configure your transactions to start at the start of this service method and to end at the end of this service method. Spring can make this happen for you if you annotate your service class with @Transactional. See http://static.springframework.org/spring/docs/2.0.x/reference/transaction.html. In my example I used a UserDao just for simplicity, but in practice you should never call a DAO directly from a controller class. So now your controller is not part of the transaction and will not cause any database updates. This will require you to call service.updateUser() or something to explicitly apply changes to the database.

  10. May 18, 2009 at 15:08 by Jelmer Kuperus

    @Tom, I dont think using transactions will actually solve this problem. As long as the hibernate session stays open changes will be written to the database when the session is flushed, transaction or no transaction.

    Most people seeing this problem are using the open session in view pattern. This keeps the hibernate session open for the entire duration of the request. So if you read an object from a service layer and bind on it, these changes will actually be persisted. regardless of whether validation failed etc

    There are two solutions,

    1) make your service layer transactional and mark methods that return objects you bind on as readonly. This will set the hibernate flushmode to manual (or something along those lines) so changes wont actually be stored unless you explicitly flush the session

    2) dont use open session in view. If you have issues with lazy initialization exceptions just eager fetch those relations for that specific call. (join fetch)

    These days I have a strong preference for 2

  11. May 19, 2009 at 16:05 by Tom van Zummeren

    @Jelmer: you are right, thanks for the input. When I said transaction I kind of implied a hibernate session. Because in typical applications I write, those two tend to begin and end at the same time. Especially when using JPA (right?)

  12. May 27, 2009 at 06:42 by PB

    How do you unit test your submit method if you have to include a BindingResult in its signature?

  13. June 1, 2009 at 17:03 by nyuby

    Hii,,i have a problem when using multiple @ModelAttribute in my contrroler. I have two class Person and address, they are have one-to-one relationship. This my view :
    Name

    Street

    City

    and this my controller to handle get method :

    @RequestMapping(method = RequestMethod.GET)
    private ModelMap displayForm(@RequestParam(value = "id", required = false) Long id) {
    Person person;
    Address address ;
    if (id == null) {
    person = new Person();
    address = new Address();
    person.setAdress(address);
    } else {
    person = personDao.getById(id);
    if (person.getAddress == null) {
    address = new Adress();
    mahasiswa.setAddress(address);
    }
    }
    return new ModelMap(person);
    }

    if i request get method, everything is ok, but if i request post method i get problem, like this :

    org.springframework.beans.NullValueInNestedPathException: Invalid property 'address' of bean class [entity.Person]: Value of nested property 'address' is null

    this is my controller to handle post method :

    @RequestMapping(method = RequestMethod.POST)
    private String proccessForm(@ModelAttribute("person") Person person, BindingResult resultPerson,@ModelAttribute("address") address) {
    person.setAddress(address);
    newPersonValidator().validate(person, resultPerson);
    if (resultPerson.hasErrors()) {
    return "inputPerson";
    } else {
    personDao.save(person);
    return "listPerson";
    }
    }

    if i using velocity as view it's ok, i get the address , i didn't where is my wrong,could you give me some explanation, what is about view ? Thanks before...

  14. [...] to implement a reference webshop implementation. Our web frontends are based on Spring@MVC, see Tom’s blog post about that, with JQuery for the scripting. And we use the Google Maps API to display the location of [...]

  15. October 15, 2009 at 22:38 by Aseem

    In the validation part,,how will it recognize what "formView" is?

    if (bindingResult.hasErrors()) {
    05. return new ModelAndView(“formView”);
    06. }

  16. [...] controllers maintainable , otherwise this flexibility would be very reason for troubles.  This blog has a very good suggestions on designing controllers with Spring MVC annotations using Spring [...]

  17. December 2, 2009 at 10:48 by Schoenobates

    Nice article - thanks for this.
    #S!

  18. May 19, 2010 at 18:29 by Brad

    Thanks! I've been looking for a good article about annotation-based form handling. Thanks for publishing this.

  19. May 20, 2010 at 01:14 by Brett

    How would you setup your form tag in the jsp page for this example? Also, could you provide the servlet xml file?

  20. [...] Shared Simple forms with Spring MVC 2.5 « JTeam Blog / JTeam: Enterprise Java, Open Source, software solut.... [...]

  21. November 11, 2010 at 15:23 by Amit Kumar

    First of all, thanks a lot for this nice article.

    Please look into the following scenario also:

    class UserBean {
    ...
    private List addressBeans;
    ...
    getter/setter
    .....
    }

    class AddressBean {
    ...
    private String city;
    ...
    getter/setter
    .....
    }

    @RequestMapping(method = RequestMethod.POST)
    public ModelAndView processSubmit(@ModelAttribute UserBean userBean) {
    ....
    ....
    }

    Problem Statement:
    When I submit the form and it calls abovementioned processSubmit() method, it will not automatically populate the List of AddressBeans. e.g. I am submitting 4 addressBean's city from my form and if I explicity initilize 4 AddressBean in the constructor of UserBean. Only then it works correctly otherwise it throws
    >> ......Value of nested property ‘addressBean’ is null...

    Please let me know how can it knows the number of objects coming from the form and how can it automatically populate those objects?

  22. February 4, 2011 at 21:33 by Lyndon

    Hi

    Great article, thanks very much. Makes things much clearer.

    But, what about cancelling a form?

    How does that happen?

    I've not found an explanation yet anywhere on the web!

  23. April 13, 2011 at 09:27 by marioosh

    Good article. It helps me understanding @ModelAttribute. Thanks!:)

  24. February 18, 2012 at 12:17 by Mandeep

    Hi,
    This post is really helpful for means this is as nowadays I am learning Spring first time for my new project and it really helped me by giving all the basic question's answers which I had in my mind while reading many tutorials as I have not found such a good explanation anywhere that too with example.
    Thanks Once again
    Regards,
    mandeep

  25. March 30, 2014 at 12:51 by A.Harish

    good article

    greatly appreciated if you would have presented it with example

Leave a Reply