Je recherche le moyen le plus simple et le plus simple de lier et de convertir des données dans Spring MVC. Si possible, sans aucune configuration XML.
Jusqu'à présent, j'ai utilisé PropertyEditors comme ceci:
public class CategoryEditor extends PropertyEditorSupport {
// Converts a String to a Category (when submitting form)
@Override
public void setAsText(String text) {
Category c = new Category(text);
this.setValue(c);
}
// Converts a Category to a String (when displaying form)
@Override
public String getAsText() {
Category c = (Category) this.getValue();
return c.getName();
}
}
et
...
public class MyController {
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Category.class, new CategoryEditor());
}
...
}
C'est simple: les deux conversions sont définies dans la même classe et la liaison est simple. Si je voulais faire une liaison générale entre tous mes contrôleurs, je pourrais toujours ajouter lignes dans ma config xml .
Mais Spring 3.x a introduit une nouvelle manière de procéder, en utilisant Converters :
Dans un conteneur Spring, ce système peut être utilisé comme alternative à PropertyEditors.
Donc, disons que je veux utiliser des convertisseurs parce que c'est "la dernière alternative". Je devrais créer deux convertisseurs:
public class StringToCategory implements Converter<String, Category> {
@Override
public Category convert(String source) {
Category c = new Category(source);
return c;
}
}
public class CategoryToString implements Converter<Category, String> {
@Override
public String convert(Category source) {
return source.getName();
}
}
Premier inconvénient: Je dois faire deux classes. Avantage: pas besoin de lancer grâce à la généricité.
Alors, comment puis-je simplement lier les convertisseurs aux données?
Deuxième inconvénient: Je n'ai pas trouvé de moyen simple (annotations ou autres possibilités de programmation) de le faire dans un contrôleur: rien de tel que someSpringObject.registerCustomConverter(...);
.
Les seules solutions que j'ai trouvées seraient fastidieuses, pas simples, et concerneraient uniquement la liaison entre contrôleurs:
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="somepackage.StringToCategory"/>
<bean class="somepackage.CategoryToString"/>
</set>
</property>
</bean>
configuration Java ( uniquement au printemps 3.1 + ):
@EnableWebMvc
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
protected void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToCategory());
registry.addConverter(new CategoryToString());
}
}
Avec tous ces inconvénients, pourquoi utiliser des convertisseurs? Est-ce que je manque quelque chose? Existe-t-il d'autres astuces dont je ne suis pas au courant?
Je suis tenté de continuer à utiliser PropertyEditors ... La liaison est beaucoup plus simple et rapide.
Avec tous ces inconvénients, pourquoi utiliser des convertisseurs? Est-ce que je manque quelque chose? Existe-t-il d'autres astuces dont je ne suis pas au courant?
Non, je pense que vous avez décrit de manière très détaillée PropertyEditor et Converter, comment chacun est déclaré et enregistré.
Dans mon esprit, les PropertyEditors ont une portée limitée - ils aident à convertir String en un type, et cette chaîne provient généralement de l'interface utilisateur. Il est donc logique d'enregistrer un PropertyEditor à l'aide de @InitBinder et de WebDataBinder.
En revanche, le convertisseur est plus générique, il est destiné à N'IMPORTE QUELLE conversion dans le système, et pas seulement aux conversions liées à l'interface utilisateur (Chaîne vers le type de cible). Par exemple, Spring Integration utilise de manière intensive un convertisseur pour convertir une charge utile de message en un type souhaité.
Je pense que pour les flux liés à l'interface utilisateur, PropertyEditors convient toujours, en particulier dans le cas où vous devez créer quelque chose de personnalisé pour une propriété de commande spécifique. Pour les autres cas, je prendrais la recommandation de Spring reference et écrirais plutôt un convertisseur (par exemple, pour convertir un id long en une entité, par exemple, comme exemple).
Le plus simple (en supposant que vous utilisiez un cadre de persistance), mais le moyen idéal n'est pas d'implémenter un convertisseur d'entité générique via l'interface ConditionalGenericConverter
qui convertira les entités à l'aide de leurs métadonnées.
Par exemple, si vous utilisez JPA, ce convertisseur peut rechercher si la classe spécifiée a @Entity
annotation et utilisez @Id
champ annoté pour extraire des informations et effectuer la recherche automatiquement en utilisant la valeur de chaîne fournie comme identifiant pour la recherche.
public interface ConditionalGenericConverter extends GenericConverter {
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
ConditionalGenericConverter
est une "arme ultime" de l'API de conversion Spring, mais sa mise en œuvre une fois qu'il sera capable de traiter la plupart des conversions d'entités, permettant ainsi au développeur de gagner du temps. C'est un grand soulagement lorsque vous spécifiez simplement des classes d'entités contrôleur et ne pensez jamais à mettre en œuvre un nouveau convertisseur (sauf pour les types personnalisés et non-entités, bien sûr).
Vous pouvez en quelque sorte contourner la nécessité d'avoir deux classes de convertisseur distinctes en implémentant les deux convertisseurs en tant que classes internes statiques.
public class FooConverter {
public static class BarToBaz implements Converter<Bar, Baz> {
@Override public Baz convert(Bar bar) { ... }
}
public static class BazToBar implements Converter<Baz, Bar> {
@Override public Bar convert(Baz baz) { ... }
}
}
Vous devez toujours enregistrer les deux séparément, mais au moins, cela réduit le nombre de fichiers à modifier si vous apportez des modifications.