web-dev-qa-db-fra.com

Spring: objet de liaison avec et sans @ModelAttribute

Je suis nouveau au printemps et enregistrer un utilisateur.J'ai fait comme ça.

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

Cela a bien fonctionné, mais le problème est que je n’ai pas besoin de cet objet user dans ma page welcome.jsp, aussi pourquoi augmenter l’objet model plus lourd. Alors j’ai essayé sans @ModelAttribute, cela fonctionne aussi pour moi comme ci-dessous.

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

Donc, je veux juste savoir quels sont les avantages et les inconvénients des deux et ce qui est la meilleure pratique si je n'ai vraiment pas besoin d'objet user dans jsp. Est-ce que @ModelAttribute fait autre chose que d'ajouter un objet à Model, ce qui fait que la liaison implicite ne le fait pas.Est-ce que @ModelAttribute est un moyen plus sûr de lier ou autre?

Je veux classer ma requête dans les 4 types de requête suivants. Quelle serait la différence avec et sans @ModelAttribute si je n'ai pas besoin d'envoyer de données en vue et que ma requête est quelconque-

  1. chaîne de requête, c'est-à-dire les données de formulaire dans GET
  2. demander une charge ou un corps, c'est-à-dire des données de formulaire dans POST
  3. données json dans ajaxified GET requst
  4. json data in POST requst - Je suppose que cela ne serait lié dans aucun des deux. @RequestBody est requis.
11
TheCurious

Il n'y a probablement (voir ci-dessous ...) aucune différence de comportement entre les deux signatures de méthode dans votre cas.

Les deux lieront les paramètres de requête à user et ajouteront l'objet résultant au modèle en tant qu'attribut user - ce nom d'attribut étant dérivé du nom de type décapitalisé de l'argument de méthode, User.

@ModelAttribute peut être utilisé pour personnaliser le nom de l'attribut, par exemple. @ModelAttribute("theUser"), ou pour indiquer au lecteur de votre code que cet argument est utilisé dans la vue. Mais comme vous le dites, aucun de ces cas ne s'applique à votre cas d'utilisation.

Le même code dans Spring sera utilisé pour renseigner l'argument, que vous utilisiez ou non l'annotation @ModelAttribute - le code en question est org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.

Il est donc plus logique que vous utilisiez la signature public String register(User user, BindingResult result) dans votre code. L'ajout d'une annotation @ModelAttribute à des arguments de méthode non requis dans le modèle peut prêter à confusion pour les personnes qui lisent votre code.


La réponse un peu plus longue est que à peu près tout pourrait être une raison de spécifier @ModelAttribute dans votre cas - mais c'est assez obscur et peu probable.

Les arguments de méthode dans les méthodes du gestionnaire Spring sont renseignés par des instances HandlerMethodArgumentResolver. Celles-ci sont configurables et sont essayées tour à tour pour chaque paramètre.

Les résolveurs d'arguments de la méthode de gestionnaire par défaut ressemblent à ceci (voir RequestMappingHandlerAdapter):

resolvers.add(new ServletModelAttributeMethodProcessor(false));

...

resolvers.add(new ServletModelAttributeMethodProcessor(true));

Si vous deviez ajouter le vôtre au milieu, par exemple UserHandlerMethodArgumentResolver, vous pouvez ensuite utiliser @ModelAttribute pour indiquer à Spring de traiter un argument spécifique de la manière par défaut, plutôt que d'utiliser votre classe de résolution d'arguments personnalisée.

4
ryanp

Cette question est très utile, mais je ne vois pas la réponse ici répondre correctement à la question.

J'ai lu plus de sujets dans stackoverflow, et j'ai trouvé celui-ci très utile: https://stackoverflow.com/a/26916920/1542363

Pour moi-même, comment décider lequel utiliser, si je n'ai besoin que de la liaison et que je ne veux pas stocker l'objet paramètre dans le modèle, n'utilisez pas @ModelAttribute.

1
GMsoF

Vérifiez ce post ici . Il couvre une bonne quantité de détails sur ModelAttribute.

ModelAttribute ne peut être utilisé qu'avec des données de demande codées par formulaire. Il ne peut pas lier les données de requête json/xml avec les objets de données. Pour cela, vous devrez utiliser RequestBody.

1
praveenj

En plus de l'ajout d'objet à Model, Spring MVC l'utilise pour fournir l'objet lié à une méthode Controller où vous pouvez l'utiliser, dans votre cas, pour "enregistrer". 

Et oui @ModelAtttribute est le moyen le plus sûr et le plus efficace de Spring MVC pour lier les données de publication entrantes à un objet.

1

Comme décrit dans la documentation Spring MVC, l'annotation @ModelAttribute peut être utilisée sur des méthodes ou sur des arguments de méthode. Et bien sûr, nous pouvons avoir les deux à utiliser en même temps dans un contrôleur.

Annotation de méthode

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

Le but de cette méthode est d’ajouter un attribut dans le modèle. Donc, dans notre cas, la clé de personne aura un objet de personne comme valeur dans le modèle. Les méthodes @ModelAttribute d'un contrôleur sont appelées avant les méthodes @RequestMapping, au sein du même contrôleur.

Argument de la méthode

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

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

0
shubham

en plus de la réponse parfaite de @ryanp, j'aimerais ajouter:

pour le projet mvc moderne de Spring, il est presque certain que les annotations telles que @Controller et @RequestMapping, etc. serviront à fournir un gestionnaire de demandes, en interne. Spring MVC utilise RequestMappingHandlerAdapter.invokeHandlerMethod () pour traiter la demande avec HandlerMethod fourni par l'utilisateur. Si vous examinez RequestMappingHandlerAdapter, il configure une collection de résolveurs d'arguments pour préparer l'argument de HandlerMethod. En examinant l'ensemble, vous comprenez comment et dans quel ordre Spring MVC analyse-t-il la requête et remplit-il les arguments fournis par l'utilisateur. alors voici le code source:

`` `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;
}

`` `

Ce qui mérite d'être noté, ce sont les résolveurs Catch-all en bas. Spring MVC utilise les deux mêmes résolveurs que @RequestParam et @ModelAttribute pour gérer les types simples non annotés et les arguments de types pojo, respectivement. C'est pourquoi dans le test de OP, peu importe qu'il y ait ou non @ModelAttribute.

C'est dommage que ce ne soit pas clair comme du cristal dans la référence du Spring MVC.

0
John

Consultation de la documentation actuelle (Spring 5.1.5) ( https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-arguments ):

Argument de la méthode contrôleur - Tout autre argument [GP - sans 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.

Ainsi, si vous avez une propriété non simple en tant que paramètre dans une méthode mappée par un contrôleur, c'est l'équivalent exact de l'annoter en tant que @ModelAttribute.

0
Gilad Peleg