web-dev-qa-db-fra.com

Conversions au printemps

Je suive ce schéma dans une application de printemps.

  1. La demande est envoyée au serveur avec l'ID de l'objet et d'autres paramètres à remplir dans cet objet.
  2. L'objet avec cet identifiant est chargé de la base de données
  3. les getters and Setters sont invoqués dans cet objet pour remplir les valeurs
  4. l'objet est ensuite stocké

J'ai demandé à cette autre question Quelle était la meilleure façon de préparer l'objet avant de remplir les paramètres des paramètres de la demande. La réponse était que le meilleur moyen était d'utiliser un service de conversion au lieu de le faire dans une méthode annotée @modelatribute ou avec un éditeur de l'initbinder.

J'ai donc essayé d'utiliser un convertisseur, mais je n'ai pas trouvé d'exemple similaire et je suis un peu coincé. J'ai écrit un code comme celui ci-dessous: dans le classeur Init, j'inscrise le service de conversion. Donc, avant de remplir les valeurs de la méthode de l'objet Convert () d'objet () est invoquée pour charger l'objet dans la base de données. Le problème est que cette configuration ne fonctionne pas car elle convertissait le numéro ID (nom d'utilisateur) de l'utilisateur d'objet en un utilisateur d'objet, mais il essaie ensuite de faire un nom de sécurité () avec l'objet afin que je reçois un "java.lang .IllegalargumentException: décalage de type argument ".

Quelqu'un peut-il me donner un indice ou un exemple de la manière d'utiliser les conversions de conversation pour obtenir le comportement souhaité?

Merci.

@Autowired
private ConversionService conversionService;

@InitBinder("user")
public void initBinder(@RequestParam("username")String username, WebDataBinder binder){
    binder.setConversionService(conversionService);
}

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

avec quelque chose comme:

@Component
public class UserConversionService implements ConversionService{
    ...        
    @Override
    public Object convert(Object name, TypeDescriptor arg1, TypeDescriptor arg2) {
        return userService.find((String)name); 
    }
}
25
Javi

Vous essayez de mettre en œuvre un ConversionService pour faire la conversion entre les chaînes et les objets utilisateur. Cependant, c'est Converter mises en œuvre qui font cette partie. Ce que vous voulez faire est:

  1. Écrire un convertisseur
  2. Enregistrer ce convertisseur avec un conversions
  3. Utiliser le conversions de conversions.

Votre convertisseur serait quelque chose comme:

final class UserConverter implements Converter<String, User> {
    ...
    public User convert(String username) {
        return userService.find(username);
    }

}

Vous devez ensuite enregistrer ce convertisseur. Vous pouvez soit écrire votre propre conversionsserviceFactoryBean ou remplacer la valeur par défaut:

<bean id="conversionService"
      class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <list>
            <bean class="example.UserConverter"/>
        </list>
    </property>
</bean>

Si vous souhaitez utiliser les conversions de conversions, comme vous le souhaitez, laissez-le simplement comme quelque chose qui peut être autonome. Le printemps et cette définition d'une faussebanbean s'occuperont du reste.

Si, cependant, vous utilisez déjà le <mvc:annotation-driven> Tagez dans votre contexte, vous pouvez utiliser son conversion-service Attribut pour référence à votre conversationViceFactoryBean. Vous n'avez alors pas besoin d'avoir une initbinder ou des conversions de conversservice dans votre classe: en vous trouvant simplement un paramètre d'un @RequestMapping, vous avez votre type cible, votre utilisateur, la conversion aura lieu sans avoir à intervenir.

41
GaryF

J'ai fait exactement ce que Gary dit ci-dessus et cela a fonctionné:

Je veux ajouter encore plus d'informations à la solution. Selon la documentation de printemps ici Une variable de modèle URI se traduit par l'objet cible à l'aide du convertisseur/conversationervice. J'ai essayé d'utiliser une @RequestParam("id") @ModelAttribute("contact") Contact contact, mais je recevais un IllegalStateException: Neither BindingResult nor plain target object for bean name 'contact' available as request attribute Pour ne pas avoir l'objet de modèle contact dans mon avis edit.jsp. Cela peut être facilement résolu en déclarant un Model model Et model.addAttribute(contact);. Cependant, il y a un moyen encore meilleur; en utilisant la variable de modèle URI. C'est étrange pourquoi @RequestParam N'a pas fonctionné.

N'A PAS MARCHÉ

@RequestMapping("edit") //Passing id as .. edit?id=1
public String editWithConverter(@RequestParam("id") @ModelAttribute("contact") Contact contact){
    logger.info("edit with converter");
     return "contact/edit";
}

Ce qui a fonctionné

@RequestMapping("edit/{contact}") //Passing id as .. edit/1
public String editWithConverter(@PathVariable("contact") @ModelAttribute("contact") Contact contact){ // STS gave a warning for using {contact} without @PathVariable 
    logger.info("edit with converter");
     return "contact/edit";
}

Alors, que fait cette chose .. un lien comme ...edit/1 Invoque implicitement le convertisseur de la chaîne '1' à contacter de la conversion ID '1' et apporte cet objet de contact à la vue. Pas besoin de @InitBinder Et depuis son Converter enregistré avec le ConversionService je peux utiliser ceci n'importe où je veux - implicitement ou explicitement .

4
Ajay