MVC de printemps: Veuillez expliquer la différence entre @RequestParam et @ModelAttribute

je suis nouveau à Spring MVC. Aidez-moi à déballer la documentation.

Documentation

Spring MVC Documentation les états (l'emphase est mienne):

  • @ModelAttribute sur un argument de méthode indique l'argument doit être récupéré à partir du modèle. Si pas présent dans le modèle, l'argument doit être instancié en premier et ensuite ajouté à modèle. Une fois présent dans le model, les champs de l'argument doivent être remplis à partir de tous les paramètres de requête qui ont des noms correspondants . La classe WebDataBinder fait correspondre les noms de paramètres de requête - y compris les paramètres de la chaîne de requête et les champs de forme - aux champs d'attributs du modèle par nom.

  • @RequestParam lie les paramètres de requête à un paramètre de méthode dans votre controller.

Disclaimer / Clarificateur

je sais que @ModelAttribute et @RequestParam ne sont pas la même chose, ne sont pas mutuellement exclusifs, ne remplissent pas le même rôle, et peuvent être utilisés simultanément, comme dans cette question - en effet, @RequestParam peut être utilisé pour peupler les champs de @ModelAttribute . Ma question Est davantage axée sur la différence entre leur fonctionnement interne.

Question:

Quelle est la différence entre @ModelAttribute (utilisé sur un argument de méthode, pas de méthode) et @RequestParam ? Plus précisément:

  • Source: Do @RequestParam et @ModelAttribute ont la même source de information / population, c.-à-d. les paramètres de demande dans L'URL, qui peuvent avoir été fournis comme éléments d'un formulaire / modèle qui était POST ed?
  • Usage: est-il correct que les variables récupérées avec @RequestParam soient jetées (sauf si passées dans un modèle), alors que les variables récupérées avec @ModelAttribute sont automatiquement introduites dans le modèle à retourner?

ou dans des exemples de codage très basiques, Quelle est la véritable différence de fonctionnement entre ces deux exemples?

exemple 1: @RequestParam :

// foo and bar are thrown away, and are just used (e.g.) to control flow?
@RequestMapping(method = RequestMethod.POST)
public String testFooBar(@RequestParam("foo") String foo,
@RequestParam("bar") String bar, ModelMap model) {
    try {
     doStuff(foo, bar);
    }
    // other code
  }

exemple 2: @ModelAttribute :

// FOOBAR CLASS
// Fields could of course be explicitly populated from parameters by @RequestParam
public class FooBar{
    private String foo;
    private String bar;
   // plus set() and get() methods
}

// CONTROLLER
// Foo and Bar become part of the model to be returned for the next view?
@RequestMapping(method = RequestMethod.POST)
public String setupForm(@ModelAttribute("fooBar") FooBar foobar) {
   String foo = fooBar.getFoo();
   String bar = fooBar.getBar();
   try {
      doStuff(foo, bar);
   }
   // other code
}

ma compréhension actuelle:

@ModelAttribute et @RequestParam à la fois d'interroger les paramètres de la demande pour l'information, mais ils utilisent cette information de manière différente:

  • @RequestParam il suffit de peupler des variables autonomes (qui peuvent bien sûr être des champs dans une classe @ModelAttribute ). Ces variables seront jeté lorsque le contrôleur est terminé, à moins qu'ils n'aient été introduits dans le modèle.

  • @ModelAttribute "popule les champs d'une classe, qui ensuite popule un attribut du modèle à passer en arrière à la vue

est-ce correct?

39
demandé sur Community 2015-03-31 17:07:37

5 réponses

@RequestParam ne fait que peupler des variables autonomes (qui peuvent bien sûr être des champs dans une classe @ModelAttribute ). Ces variables seront rejetées lorsque le contrôleur est terminé, à moins qu'elles n'aient été introduites dans le modèle.

ne confondez pas le mot" model " avec session. La conversation http est généralement: HTTP.GET , réponse du serveur, puis HTTP.POST . Lorsque vous avez l'annotation @ModelAttribute en cours d'utilisation vous êtes toujours construire une instance de tout ce que vous avez annoté, c'est ce qui vous fait penser que "nourrir des choses au modèle" pourrait faire coller des variables. Ce n'est pas correct, une fois que le HttpServletRequest a terminé ces variables ne devraient plus faire partie de la conversation navigateur/serveur à moins qu'ils n'aient été enregistrés dans une session.

@ModelAttribute popule les champs d'une classe, qui ensuite popule un attribut du modèle à transmettre à la view

Oui! Pour être correct, @ModelAttribute dit à Spring d'utiliser son liant de données Web par défaut pour peupler une instance de quelque chose avec des données du HttpServletRequest . Le choix de transmettre ces données à la vue est au programmeur. Quand vous avez une méthode annotée avec @ModelAttribute , elle est appelée chaque fois que le code frappe ce servlet. Lorsque vous avez @ModelAttribute comme paramètre de la méthode, nous parlons de la liaison de données entrante du formulaire Http.

appeler @RequestParam est un raccourci pour dire request.getParameter("foo") ; sous le capot, HttpServletRequest de Java vous permet d'obtenir des valeurs à partir de l'objet request en faisant une recherche de clé->valeur. La valeur retournée est de type Objet. C'est ce que vous taperiez beaucoup si vous n'utilisiez pas Spring dans votre application web.

Spring prend alors cette abstraction un peu plus loin quand vous commencez à utiliser @ModelAttribute . Cette annotation utilise le concept de de liaison de données. Le but de data-binding est que le code dans votre controller n'aura pas à appeler request.getParameter("foo1") , pour chaque élément de forme. Imaginez que vous avez un formulaire web avec 5 champs. Sans liaison de données, le programmeur doit extraire et valider manuellement chacun de ces champs. Le programmeur doit s'assurer que la requête contient la propriété, que la valeur de la propriété existe, et que la valeur de la propriété est de type attendu pour chaque champ. Utiliser @ModelAttribute indique à Spring de faire ce travail pour vous.

si vous annotez une méthode dans votre controller avec @ModelAttribute("fooBar") FooBar fooBar une instance de FooBar sera toujours être construit par ressort, et fourni à votre méthode. Lorsque la liaison de données entre en jeu, c'est lorsque cette annotation est utilisée dans les paramètres D'une méthode; Spring regarde l'instance de HttpServletRequest et voit si elle peut faire correspondre les données dans la requête à la propriété de droite sur une instance de FooBar . C'est basé sur la Convention des propriétés java, où vous avez un champ tel que foo et" public getters and setters called getFoo et setFoo . Cela peut sembler magique mais si vous deviez briser la convention, votre liaison de données de printemps cesserait de fonctionner parce qu'il ne serait pas en mesure de savoir pour lier les données de votre HttpServletRequest vous obtiendriez toujours une instance de FooBar , mais les propriétés ne seraient pas définies à des valeurs de la requête.

30
répondu zmf 2017-12-21 22:02:11

@ModelAttribute les paramètres annotés sont traités par un ServletModelAttributeMethodProcessor enregistré (ou ModelAttributeMethodProcessor ) et @RequestParam les paramètres annotés sont traités par un RequestParamMethodArgumentResolver enregistré ou RequestParamMapMethodArgumentResolver selon le type de paramètre.

voici une explication de la façon dont Spring utilise ces HandlerMethodArgumentResolvers pour résoudre les arguments pour vos méthodes de handler:

dans les deux cas, @ModelAttribute et @RequestParam , les valeurs à relier sont extraites de ServletRequest paramètres .

Vous pouvez regarder le code source des types mentionnés ci-dessus, mais voici les détails simples.

pour @ModelAttribute , le ressort créera une instance du type de paramètre. Il inspectera les champs de cette instance et tentera de lier les valeurs des paramètres à ils sont basés sur une stratégie de nommage/aliasing composée du nom @ModelAttribute et des noms de champ. Il utilise généralement un groupe d'instances Converter pour passer de String (les valeurs paramétriques sont toujours String ) à n'importe quel type de champ cible Integer , Date , etc. Vous pouvez également enregistrer vos propres types Converter pour les conversions personnalisées. Vous pouvez également nicher des types de POJO.

pour @RequestParam , le ressort utilisera le même Converter instances pour passer directement des valeurs des paramètres au type de paramètre annoté.

notez que les valeurs des paramètres ne sont pas "jetées". Ils sont stockés dans le HttpServletRequest pendant la durée du cycle de traitement des demandes du conteneur. Vous pouvez toujours y accéder par le méthodes appropriées .

2
répondu Sotirios Delimanolis 2017-05-23 11:55:19

@ModelAttribute : lie un objet Java entier (comme un employé). prend en charge plusieurs paramètres de requête

@RequestParam : lie un paramètre de requête unique (comme firstName)

en général,

@RequestParam est le meilleur pour lire un petit nombre de params.

@ModelAttribute est utilisé lorsque vous avez un formulaire avec un grand nombre de champs.

et

@ModelAttribute vous donne des fonctionnalités supplémentaires telles que la liaison de données, la validation et la forme préremplissage.

2
répondu Harinath 2017-11-03 09:01:49

@ModelAttribute (paramètre) charge un attribut de modèle de @SessionAttributes ou de @ModelAttribute (méthode) .

vous n'en avez pas besoin juste pour lier les valeurs d'une requête, mais il le fera après le chargement de @SessionAttributes .

@RequestParam lie un paramètre de requête à un objet.

0
répondu Neil McGuigan 2015-03-31 18:27:12
  • au niveau de la méthode

Lorsque l'annotation est utilisée au niveau de la méthode, il indique le but de cette méthode est d'ajouter un ou plusieurs attributs de modèle. De telles méthodes supportent les mêmes types d'arguments que les méthodes @RequestMapping mais qui ne peuvent pas être mappées directement aux requêtes.

@ModelAttribute
public void addAttributes(Model model) {
    model.addAttribute("msg", "Welcome to the Netherlands!");
}

méthode qui ajoute un attribut nommé msg à tous les modèles définis dans la classe controller.

Spring-MVC fera toujours un appel en premier à cette méthode, avant qu'elle n'appelle des méthodes de gestionnaire de requêtes. @ModelAttribute méthodes sont invoquées devant le contrôleur méthodes annotées avec @RequestMapping sont invoqués. la logique derrière la séquence est que, l'objet model doit être créé avant que n'importe quel traitement commence à l'intérieur des méthodes de controller.

il est également important que vous annotiez la classe respective comme @ControllerAdvice. Ainsi, vous pouvez ajouter des valeurs dans Model qui seront identifiées comme globales. Cela signifie en fait que pour chaque requête une valeur par défaut existe, pour chaque méthode dans la partie de réponse.

  • Comme un Argument de Méthode

Lorsqu'il est utilisé comme argument de méthode, il indique que l'argument doit être récupéré à partir du modèle. s'il n'est pas présent, il doit d'abord être instancié puis ajouté au Modèle et une fois présent dans le modèle, les champs arguments devraient être rempli à partir de tous les paramètres de requête qui ont des noms correspondants.

dans l'extrait de code qui suit l'attribut user model est rempli avec les données d'un formulaire soumis au point final addUser. Printemps MVC fait cela dans les coulisses avant d'invoquer la méthode submit:

**@RequestMapping**(value = "/addUser", method = RequestMethod.POST)
public String submit(@ModelAttribute("user") User user) {
    return "userView";
}

ainsi, il lie les données de forme avec un haricot. Le contrôleur annoté avec @RequestMapping peut avoir des arguments de classe personnalisés annotés avec @ModelAttribute.

c'est ce qu'on appelle communément la liaison de données au printemps-MVC, un mécanisme commun qui vous évite d'avoir à analyser chaque champ de formulaire individuellement.

0
répondu Kumar Abhishek 2017-03-10 18:11:15