2012-09-22 23 views
120

Estoy buscando la forma más fácil y simple de enlazar y convertir datos en Spring MVC. Si es posible, sin hacer ninguna configuración xml.Conversión de tipo Spring MVC: ¿PropertyEditor o convertidor?

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: tanto la conversión se definen en la misma clase, y la unión es directa. Si quisiera hacer un enlace general en todos mis controladores, podría agregar 3 lines in my xml config.


pero la primavera 3.x introdujo una nueva manera de hacerlo, usando Converters:

dentro de un contenedor de primavera, este sistema puede ser utilizado como una alternativa a PropertyEditors

Digamos que quiero usar Convertidores porque es "la última alternativa". Tendría que crear dos convertidores:

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

} 

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

Entonces, ¿cómo puedo simplemente vincular los datos a los convertidores?

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

Las únicas maneras que he encontrado sería tedioso, no es simple, y sólo alrededor de la unión cruzada controlador en general:

  • XML config:

    <bean id="conversionService" 
        class="org.springframework.context.support.ConversionServiceFactoryBean"> 
        <property name="converters"> 
         <set> 
          <bean class="somepackage.StringToCategory"/> 
          <bean class="somepackage.CategoryToString"/> 
         </set> 
        </property> 
    </bean> 
    
  • Java config (sólo en primavera 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.

+0

Nota (me tropecé también, usando Spring 3.2.17): cuando se usa hay una necesidad de referirse a esta conversión BeanService: mauhiz

Respuesta

50

Con todos estos inconvenientes, ¿por qué usar Convertidores? ¿Me falta 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 de ellos.

En mi opinión, los PropertyEditors tienen un alcance 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 (Cadena a tipo de destino). 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).

+5

Otra cosa buena que los convertidores son apátridas, mientras que los editores de propiedades son con estado y creados muchas veces e implementados con muchas llamadas a API, no creo que esto tendrá un gran impacto en el rendimiento, pero los convertidores son más limpios y simples. –

+1

@Boris cleaner Sí, pero no es más sencillo, especialmente para un principiante: debe escribir 2 clases de convertidor + agregar varias líneas de configuración xml o configuración java. Estoy hablando de la presentación/visualización de formularios Spring MVC, con conversiones generales (no solo entidades). –

7

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

Por ejemplo, si está utilizando JPA, este convertidor puede mirar si la clase especificada tiene @Entity anotación, y utilizar @Id campo anotado para extraer información y realizar la búsqueda utilizando automáticamente el valor de cadena suministrada como un identificador para las operaciones de búsqueda.

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

ConditionalGenericConverter es un "arma definitiva" de la API de conversión de primavera, pero están aplicando una vez que será capaz de procesar la mayoría de conversiones entidad, ahorrando tiempo de desarrollo - es un gran alivio cuando se acaba de especificar las clases de entidad como parámetros de su controlador y nunca piense en implementar un nuevo convertidor (excepto para tipos personalizados y no de entidades, por supuesto).

+0

Buena solución para tratar solo con la conversión de entidades, gracias por el truco. No es simple al principio, ya que tienes que escribir una clase más, pero simple y de tiempo a la larga. –

+0

Por cierto, un convertidor de este tipo se puede implementar para cualquier tipo que cumpla con algún contrato genérico; otro ejemplo: si sus enums implementan una interfaz de búsqueda inversa común, entonces también podrá implementar un convertidor genérico (será similar a http: //stackoverflow.com/questions/5178622/spring-custom-converter-for-all-enums) –

+0

@JeromeDalbert Sí, es un poco difícil para un principiante hacer cosas pesadas, pero si tiene un equipo de desarrolladores lo hará ser más simple) PD Y se volverá aburrido registrar los mismos editores de propiedades cada vez en encuadernación de formularios de todos modos) –

14
  1. Para a/de las conversiones de cadena utilizan formateadores (implementar org.springframework.format.Formatter) en lugar de los convertidores. Tiene de impresión (...) y de análisis (...) métodos, por lo que necesita una sola clase en lugar de dos. para registrarlos, utilice FormattingConversionServiceFactoryBean, que puede registrar ambos convertidores y formateadores, en lugar de ConversionServiceFactoryBean.
  2. El nuevo material formateador tiene un par de ventajas adicionales:
    • interfaz formateador suministra el objeto Locale en su de impresión (...) y de análisis (...) métodos, por lo que su conversión de cadenas puede ser sensible a la localidad
    • Además de los formateadores registrados previamente, FormattingConversionServiceFactoryBean viene con un par de mano preregistered AnnotationFormatterFactory objetos, que le permiten especificar un formato adicional p arameters a través de la anotación. Por ejemplo: @RequestParam @DateTimeFormat (patrón = "MM-dd-aa") LocalDate fechabase ... No es muy difícil crear sus propias AnnotationFormatterFactory clases, consulte NumberFormatAnnotationFormatterFactory de primavera para un ejemplo simple. Creo que esto elimina la necesidad en los formateadores/editores específicos del controlador. Use un ConversionService para todos los controladores y personalice el formato mediante anotaciones.
  3. Acepto que si aún necesita alguna conversión de cadena específica del controlador, la forma más simple es seguir usando el editor de propiedades personalizado. (Intenté llamar a 'binder.setConversionService (...)' en mi método @InitBinder, pero falla, ya que el objeto de enlace viene con el servicio de conversión 'global' ya configurado. Parece una conversión por controlador las clases se desaconsejan en primavera 3).
0

Puede evitar 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<Bar, Baz> { 
     @Override public Baz convert(Bar bar) { ... } 
    } 
    public static class BazToBar implements Converter<Baz, Bar> { 
     @Override public Bar convert(Baz baz) { ... } 
    } 
} 

usted todavía necesita registrarse tanto de ellos por separado, pero al menos Esto reduce el número de archivos que necesita para modificar si se realiza algún cambio.

Cuestiones relacionadas