Spring : binding object with and without @ModelAttribute
Asked Answered
N

7

16

I am new in Spring and registering a user.I did like this.

@RequestMapping("/register")
    public String register(@ModelAttribute User user,BindingResult result){
       if(!result.hasErrors()){
         userSerive.register(user);
       }
     return "welcome";
}

This worked fine,but problem here is I don't need this user object in my welcome.jsp page,so why make model object heavier.So I tried without @ModelAttribute, this also works for me like below.

@RequestMapping("/register")
    public String register(User user,BindingResult result){
       if(!result.hasErrors()){
         userSerive.register(user);
       }
     return "welcome";
}

So I just want to know what are pros & cons of both and which is the best practice if I really don't need user object in jsp. Is @ModelAttribute does any other thing apart from adding object to Model,which spring implicit binding not does.Is @ModelAttribute safer way of binding or else?

I want to categories my query in following 4 type of request.what would be difference with and without @ModelAttribute if I need not to send data in view and my request is any of-

  1. query string ie form data in GET
  2. request payload or body ie form data in POST
  3. json data in ajaxified GET requst
  4. json data in POST requst- I guess this would not be bind in any of both. @RequestBody is required.
Nigeria answered 25/2, 2017 at 22:41 Comment(1)
You can’t start a bounty on this question because bounties have to go up in value each time and the next increment is 100 rep.Yaker
E
7

There is probably (see below...) no difference in the behaviour between the two method signatures in your case.

Both will bind the request parameters to user and add the resulting object to the model as the attribute user - this attribute name being derived from the decapitalised type name of the method argument, User.

@ModelAttribute can be used to customise the name of the attribute, e.g. @ModelAttribute("theUser"), or to give a hint to the reader of your code that this argument is used in the view. But as you say, neither of these apply in your use case.

Exactly the same code in Spring will be used to populate the argument whether you use the @ModelAttribute annotation or not - the code in question is org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.

It therefore makes more sense to me for you to use the public String register(User user, BindingResult result) signature in your code. Adding a @ModelAttribute annotation to method arguments that are not required in the model could be confusing to people reading your code.


The slightly longer answer is that there could just about be a reason for specifying @ModelAttribute in your case - but it's quite arcane and unlikely.

Method arguments in Spring handler methods are populated by HandlerMethodArgumentResolver instances. These are configurable and are attempted in turn for each parameter.

The default handler method argument resolvers look like this (see RequestMappingHandlerAdapter):

resolvers.add(new ServletModelAttributeMethodProcessor(false));

...

resolvers.add(new ServletModelAttributeMethodProcessor(true));

If you were to add your own in the middle, e.g. a UserHandlerMethodArgumentResolver, you could then use @ModelAttribute to tell Spring to process a specific argument in the default way, rather than use your custom argument resolver class.

Ette answered 6/3, 2017 at 7:59 Comment(1)
This is the answer to the OP's question.Pisolite
C
5

This question is very useful, but I don't see the reply here answer the question properly.

I read through more threads in stackoverflow, and found this one very useful: https://mcmap.net/q/81892/-modelattribute-annotation-when-to-use-it

For myself how to decide which one to use, if I only need the binding and do not want to store the parameter object in model, then don't use @ModelAttribute.

Catechist answered 29/3, 2017 at 2:13 Comment(1)
I havnt found any reason of storing the parameter object in model apart from sending to views like jsp.yes my question is all about is there any other reason for same.Nigeria
R
1

Apart from adding object to Model, Spring MVC uses it to supply the bound object to a Controller method where you can use it, in your case to "register".

And yes @ModelAtttribute is the safest and best way in Spring MVC to bind incoming post data to an object.

Repeat answered 9/3, 2017 at 17:43 Comment(0)
F
1

Check this post here. It covers a good amount of details on ModelAttribute.

ModelAttribute can only be used with form-encoded request data. It cannot bind json/xml request data with the data objects. For that, you will have to use RequestBody.

Freeborn answered 9/3, 2017 at 19:54 Comment(0)
P
1

in addition to @ryanp 's perfect answer, I'd like to add:

for modern spring mvc project, it's most certain one would use annotations like @Controller and @RequestMapping etc to provide a request handler, internally, Spring MVC uses RequestMappingHandlerAdapter.invokeHandlerMethod() to process the request with the user provided HandlerMethod. if you look at RequestMappingHandlerAdapter, it sets up a collection of argument resolver to prepare the argument for the HandlerMethod, by looking at the set, you understand how and in what order does Spring MVC parse request and populate the user provided arguments. so here's the source code:

```Java

/**
 * Return the list of argument resolvers to use including built-in resolvers
 * and custom resolvers provided via {@link #setCustomArgumentResolvers}.
 */
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
    List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();

    // Annotation-based argument resolution
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
    resolvers.add(new RequestParamMapMethodArgumentResolver());
    resolvers.add(new PathVariableMethodArgumentResolver());
    resolvers.add(new PathVariableMapMethodArgumentResolver());
    resolvers.add(new MatrixVariableMethodArgumentResolver());
    resolvers.add(new MatrixVariableMapMethodArgumentResolver());
    resolvers.add(new ServletModelAttributeMethodProcessor(false));
    resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
    resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
    resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new RequestHeaderMapMethodArgumentResolver());
    resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new SessionAttributeMethodArgumentResolver());
    resolvers.add(new RequestAttributeMethodArgumentResolver());

    // Type-based argument resolution
    resolvers.add(new ServletRequestMethodArgumentResolver());
    resolvers.add(new ServletResponseMethodArgumentResolver());
    resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
    resolvers.add(new RedirectAttributesMethodArgumentResolver());
    resolvers.add(new ModelMethodProcessor());
    resolvers.add(new MapMethodProcessor());
    resolvers.add(new ErrorsMethodArgumentResolver());
    resolvers.add(new SessionStatusMethodArgumentResolver());
    resolvers.add(new UriComponentsBuilderMethodArgumentResolver());

    // Custom arguments
    if (getCustomArgumentResolvers() != null) {
        resolvers.addAll(getCustomArgumentResolvers());
    }

    // Catch-all
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
    resolvers.add(new ServletModelAttributeMethodProcessor(true));

    return resolvers;
}

```

What's worth noted is the Catch-all resolvers at the bottom. Spring MVC uses the very same two resolvers that handle @RequestParam and @ModelAttribute to handle non-annotated simple types and pojo types arguments respectively. That's why in OP's test, it doesn't matter if there's @ModelAttribute or not.

It's a shame that it's not made crystal clear in the Spring MVC's reference.

Pisolite answered 14/1, 2018 at 10:1 Comment(0)
S
1

Looking at current (Spring 5.1.5) documentation (https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-arguments):

Controller method argument - Any other argument [GP - without annotation]:

If a method argument is not matched to any of the earlier values in this table and
it is a simple type (as determined by BeanUtils#isSimpleProperty),
it is a resolved as a @RequestParam. Otherwise, it is resolved as a @ModelAttribute.

So if you have a non simple property as parameter in a controller mapped method, it's the exact equivalent of annotating it as @ModelAttribute.

Skelp answered 21/2, 2019 at 8:26 Comment(0)
G
0

As described in the Spring MVC documentation - the @ModelAttribute annotation can be used on methods or on method arguments. And of course we can have both use at the same time in one controller.

Method annotation

@ModelAttribute("person")
public Person getPerson(){
    return new Person();
}

Purpose of such method is to add attribute in the model. So in our case person key will have person object as value in the Model. @ModelAttribute methods in a controller are invoked before @RequestMapping methods, within the same controller.

Method Argument

public String processForm(@ModelAttribute("person") Person person){
    person.getStuff();
}

See spring documentation http://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/mvc.html#mvc-ann-modelattrib-method-args

Greenaway answered 26/2, 2017 at 9:41 Comment(1)
you are missing context.As per my use case I dont need model in view layer,I can bind formdata in java with and without @ModelAttribute,what is the difference between both approaches if we not need this model in jsp.Nigeria

© 2022 - 2024 — McMap. All rights reserved.