6

Imagine a code like this one:

@RequestMapping(value="/users", method=RequestMethod.GET)
public String list(Model model) {
    ...
}

@InitBinder("user")
public void initBinder(WebDataBinder binder) {
    binder.setDisallowedFields("password"); // Don't allow user to override the value
}

@ModelAttribute("user")
public User prepareUser(@RequestParam("username") String username){
    ...
}

@RequestMapping(value="/user/save", method=RequestMethod.POST)
public String save(@ModelAttribute("user") User user, Model model) {        
    ...
}

I use an init binder to avoid that a field can be binded and I mark a method (prepareUser()) with @ModelAttribute to prepare my User object before it is binded. So when I invoke /user/save initBinder() and prepareUser() are executed.

I have set "user" in both @InitBinder and @ModelAttribute so Spring-MVC could understand that this methods should only be applied before executing a method with @ModelAttribute("user").

The problem is that the method annotated with @ModelAttribute("user") is executed before every mapped method of this controller. For example if I invoke /users prepareUser is executed before list() method. How can I make that this preparer is only executed before save() method having all the methods in the same controller?

Thanks

Javi
  • 19,387
  • 30
  • 102
  • 135
  • What does `prepareUser` actually do? – skaffman Dec 02 '10 at 13:41
  • @skaffman it takes the username param and load the User object from database, so all the data which cannot be binded (for example password) is not overwritten with null values when it is persisted after binding – Javi Dec 02 '10 at 13:44

1 Answers1

7

That's not really what @ModelAttribute is for. If you use it as a method parameter, it puts the annotated parameter into the model (that's fine). If you put it on a method, it's called every time to provide reference data that every method in the controller should have access to.

If you want to take control of building up your User object, you have several options. The two that are most obvious to me are:

  1. Use your InitBinder method to add a new custom editor (a PropertyEditor class) for building User objects,
  2. Use the conversion service in Spring 3 to convert string usernames to User objects.
GaryF
  • 23,950
  • 10
  • 60
  • 73
  • OK I understand that it should be done in initbinder with a property editor, but I don't understand what is the real purpose for methods with @ModelAttribute annotation. Can you tell me an example for its typical use? And also, Why can be a string given as value in the annotation (for example "user" in @ModelAttribute("user")) if it is executed before every method? – Javi Dec 02 '10 at 16:22
  • 1
    @Javi Good questions. First, I don't think it *should* be done in initbinder, it's just possible. I prefer the more modern Conversion Service approach. A typical use for methods annotated with @ModelAttribute is for reference data that all methods in a controller require. To use a Pet Store as an example, a controller may have methods for listing pets and viewing individual pets, but they all need info for a Categories menu ("Rabbits", "Dogs" etc). It saves you having to repeat this in each method. – GaryF Dec 02 '10 at 19:17
  • 2
    @Javi As for why you can give it a name, it's so Spring can put the returned object into the model under that name. The example in the Spring manual is worth a look: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-modelattrib – GaryF Dec 02 '10 at 19:19
  • 1
    Though I have understood the theory, I don't get it working with a conversion service, so I've asked this other question: http://stackoverflow.com/questions/4347284/conversionservice-in-spring – Javi Dec 03 '10 at 16:25
  • @GaryF please see my question. http://stackoverflow.com/questions/30053709/customize-mapping-request-parameters-and-fields-inside-the-dto – gstackoverflow May 05 '15 at 22:05