Conversión de tipo Spring MVC: ¿PropertyEditor o convertidor?

Estoy buscando la manera más fácil y simple de enlazar y convertir datos en Spring MVC. Si es posible, sin hacer ninguna configuración xml.

Hasta ahora he estado usando PropertyEditors así:

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(); } } 

y

 ... public class MyController { @InitBinder public void initBinder(WebDataBinder binder) { binder.registerCustomEditor(Category.class, new CategoryEditor()); } ... } 

Es simple: ambas conversiones se definen en la misma clase, y el enlace es directo. Si quisiera hacer un enlace general en todos mis controladores, aún podría agregar 3 líneas en mi configuración xml .


Pero Spring 3.x introdujo una nueva forma de hacerlo, utilizando convertidores :

Dentro de un contenedor de Spring, este sistema puede usarse como una alternativa a PropertyEditors

Digamos que quiero usar Convertidores porque es “la última alternativa”. Tendría que crear dos conversores:

 public class StringToCategory implements Converter { @Override public Category convert(String source) { Category c = new Category(source); return c; } } public class CategoryToString implements Converter { @Override public String convert(Category source) { return source.getName(); } } 

Primer inconveniente: tengo que hacer dos clases. Ventaja: no es necesario transmitir gracias a la genérica.

Entonces, ¿cómo simplemente los datos se unen a los convertidores?

Segundo inconveniente: no he encontrado ninguna manera simple (anotaciones u otras facilidades programáticas) para hacerlo en un controlador: nada como someSpringObject.registerCustomConverter(...); .

Las únicas formas que he encontrado serían tediosas, no simples, y solo acerca de la vinculación general del controlador cruzado:

  • Configuración XML :

             
  • Configuración de Java ( solo en Spring 3.1+ ):

     @EnableWebMvc @Configuration public class WebConfig extends WebMvcConfigurerAdapter { @Override protected void addFormatters(FormatterRegistry registry) { registry.addConverter(new StringToCategory()); registry.addConverter(new CategoryToString()); } } 

Con todos estos inconvenientes, ¿por qué usar convertidores? Me estoy perdiendo de algo ? ¿Hay otros trucos que no conozco?

Estoy tentado de seguir usando PropertyEditors … La vinculación es mucho más fácil y rápida.

Con todos estos inconvenientes, ¿por qué usar convertidores? Me estoy perdiendo de algo ? ¿Hay otros trucos que no conozco?

No, creo que ha descrito muy exhaustivamente PropertyEditor y Converter, cómo se declara y registra cada uno.

En mi opinión, los PropertyEditors tienen un scope limitado: ayudan a convertir String a un tipo, y esta cadena normalmente proviene de la interfaz de usuario, por lo que registrar un PropertyEditor usando @InitBinder y usar WebDataBinder tiene sentido.

Por otro lado, el convertidor es más genérico, está destinado a CUALQUIER conversión en el sistema, no solo para conversiones relacionadas con la interfaz de usuario (String to target type). Por ejemplo, Spring Integration usa un convertidor extensamente para convertir una carga útil de mensaje al tipo deseado.

Creo que para los flujos relacionados con la interfaz de usuario, los editores de propiedad siguen siendo apropiados, especialmente para el caso en el que necesite hacer algo personalizado para una propiedad de comando específica. Para otros casos, tomaría la recomendación de la referencia de Spring y escribiría un convertidor en su lugar (por ejemplo, para convertir de un ID largo a una entidad, por ejemplo, como una muestra).

  1. Para conversiones desde / hacia cadenas, utilice formateadores (implemente org.springframework.format.Formatter ) en lugar de convertidores. Tiene métodos de impresión (…) y análisis (…) , por lo que solo necesita una clase en lugar de dos. Para registrarlos, use FormattingConversionServiceFactoryBean , que puede registrar conversores y formateadores, en lugar de ConversionServiceFactoryBean .
  2. El nuevo material de Formatter tiene un par de ventajas adicionales:
    • La interfaz de formateador proporciona el objeto Locale en sus métodos de impresión (…) y análisis (…) , por lo que su conversión de cadena puede ser sensible a la configuración regional
    • Además de los formateadores prerregistrados, FormattingConversionServiceFactoryBean viene con un par de objetos preinscritos de AnnotationFormatterFactory , que le permiten especificar parámetros de formato adicionales a través de la anotación. Por ejemplo: @RequestParam @DateTimeFormat (patrón = “MM-dd-aa”) LocalDate baseDate … No es muy difícil crear sus propias clases AnnotationFormatterFactory ; consulte NumberFormatAnnotationFormatterFactory de Spring para obtener un ejemplo simple. Creo que esto elimina la necesidad de formateadores / editores específicos del controlador. Use un ConversionService para todos los controladores y personalice el formato mediante anotaciones.
  3. Estoy de acuerdo en que si aún necesita alguna conversión de cadena específica del controlador, la forma más sencilla es usar el editor de propiedades personalizado. (Intenté llamar a ‘ binder.setConversionService (…) ‘ en mi método @InitBinder , pero falla, dado que el objeto de enlace viene con el servicio de conversión ‘global’ ya configurado. Parece que las clases de conversión por controlador se desaconsejan en Primavera 3).

El más simple (suponiendo que esté utilizando un marco de persistencia), pero no de la manera perfecta, es implementar un convertidor de entidades genéricas a través de la interfaz ConditionalGenericConverter que convertirá las entidades utilizando sus metadatos.

Por ejemplo, si está utilizando JPA, este convertidor puede ver si la clase especificada tiene la anotación @Entity y usar el campo @Id anotado para extraer información y realizar la búsqueda automáticamente utilizando el valor de cadena proporcionado como ID para la búsqueda.

 public interface ConditionalGenericConverter extends GenericConverter { boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType); } 

ConditionalGenericConverter es una “arma definitiva” de Spring convertion API, pero una vez implementado podrá procesar la mayoría de las conversiones de entidades, ahorrándole tiempo al desarrollador. Es un gran alivio cuando solo especifica las clases de entidades como parámetros de su controlador y nunca piensa en ellas. implementando un nuevo convertidor (excepto para tipos personalizados y sin entidad, por supuesto).

Puede solucionar la necesidad de tener dos clases de Convertidor por separado implementando los dos Convertidores como clases internas estáticas.

 public class FooConverter { public static class BarToBaz implements Converter { @Override public Baz convert(Bar bar) { ... } } public static class BazToBar implements Converter { @Override public Bar convert(Baz baz) { ... } } } 

Debería registrar ambos por separado, pero al menos esto reduce el número de archivos que necesita modificar si realiza algún cambio.