Como se analizó en la sección anterior, core.convert es un sistema de conversión de tipos de propósito general. Proporciona una API ConversionService unificada, así como una interfaz SPI Converter fuertemente tipada para implementar la lógica de conversión de un tipo a otro. El contenedor Spring utiliza este sistema para vincular los valores de las propiedades del bean. Además, tanto Spring Expression Language (SpEL) como DataBinder utilizan este sistema para vincular valores de campo. Por ejemplo, si SpEL necesita convertir Short a Long para completar el intento expression.setValue(Object bean, Object value), el core.convert realiza esta conversión.

Ahora considere los requisitos de conversión de tipos en un entorno de cliente típico, como una aplicación web o de escritorio. En dichos entornos, la conversión de String generalmente se realiza para respaldar el proceso de devolución de datos del lado del cliente, y también de regreso a String para respaldar el proceso de representación de la vista. Además, a menudo se requiere la localización de los valores String. La interfaz SPI más general core.convert Converter no aborda dichos requisitos de formato directamente. Para abordarlos directamente, Spring 3 introdujo una conveniente interfaz SPI, Formatter, que proporciona una alternativa simple y sólida a la implementación de PropertyEditor para entornos de cliente.

En general, puede utilizar la interfaz SPI Converter si necesita implementar una lógica de conversión de tipos de propósito general, por ejemplo, para convertir entre java.util.Date y . Largo. Puede utilizar la interfaz SPI Formatter si está trabajando en un entorno de cliente (como una aplicación web) y necesita analizar y generar valores de campo localizados. ConversionService proporciona una API de conversión de tipo único para ambas interfaces SPI.

Interfaz SPI Formatter

Interfaz SPI Formatter para implementar la lógica de formato de campo es simple y está fuertemente tipado. La siguiente lista muestra la definición de la interfaz Formatter:

paquete org.springframework.format; Formateador de interfaz pública<T> extiende Impresora<T>, Analizador<T> { }

Formateador se extiende desde el bloque de construcción Impresora y Analizador interfaces. El siguiente listado muestra las definiciones de estas dos interfaces:

 interfaz pública Impresora<T> { String print(T fieldValue, Locale locale); }
        importar java.text.ParseException; Analizador de interfaz pública<T> { T parse(String clientValue,
            Locale locale) lanza ParseException; }

Para crear su propio Formatter, implemente la interfaz Formatter que se mostró anteriormente. Parametrice T como el tipo de objeto que desea formatear, por ejemplo, java.util.Date. Implemente la operación print() para imprimir una instancia de T para mostrarla en la configuración regional del cliente. Implemente una operación parse() para analizar una instancia de T a partir de la representación formateada devuelta desde la configuración regional del cliente. Su Formatter debería generar excepciones ParseException o IllegalArgumentException cuando falla un intento de análisis. Asegúrese de que su implementación de Formatter sea segura para subprocesos.

Los subpaquetes format contienen múltiples implementaciones de Formatter para mayor comodidad. El paquete Number contiene NumberStyleFormatter, CurrencyStyleFormatter y PercentStyleFormatter para formatear objetos Number que utilice java.text.NumberFormat. El paquete datetime contiene un DateFormatter para formatear objetos java.util.Date con java.text.DateFormat.

El siguiente DateFormatter es un ejemplo de implementación de Formatter:

Java
paquete org.springframework.formato.datetime; La clase final pública DateFormatter implementa Formatter<Date> { patrón de cadena privada; public DateFormatter (patrón de cadena) { this.pattern = patrón; } public String print(Fecha fecha, Configuración regional) { if (fecha == nulo) { return ""; } return getDateFormat(locale).format(fecha); } análisis público de fecha (cadena formateada, configuración regional) lanza ParseException { if (formatted.length() == 0) { return null; } return getDateFormat(locale).parse(formateado); } protected DateFormat getDateFormat(Locale locale) { DateFormat dateFormat = new SimpleDateFormat(this.pattern, locale); dateFormat.setLenient(falso); formato de fecha de regreso; } }
Kotlin
clase DateFormatter(patrón de valor privado: cadena): formateador<Fecha> { anular fun print(fecha: Fecha, local: Locale) = getDateFormat(locale).format(fecha) @Throws(ParseException::class) anular fun parse(formateado: String, locale: Locale) = getDateFormat(locale).parse (formateado) protegido divertido getDateFormat(locale: Locale): DateFormat { val dateFormat = SimpleDateFormat(this.pattern, locale) dateFormat.isLenient = false return dateFormat } }

El equipo de Spring agradece la participación de la comunidad en el desarrollo de Formatter. Para hacer sus sugerencias, consulte "GitHub Issues".

Formato basado en anotaciones

El formato de campo se puede configurar por campo o tipo de anotación. Para vincular una anotación a Formatter, implemente AnnotationFormatterFactory. La siguiente lista muestra la definición de la interfaz AnnotationFormatterFactory:

paquete org.springframework.format; interfaz pública AnnotationFormatterFactory<A extiende Annotation> { Establecer<Clase<?>> getTipos de campo(); Impresora<?> getPrinter(Una anotación, Clase<?> tipo de campo); Analizador<?> getParser(Una anotación, Clase<?> tipodecampo); }

Para crear una implementación:

  1. Parametrizar A como campos annotationType , con el que desea asociar la lógica de formato, por ejemplo, org.springframework.format.annotation.DateTimeFormat.

  2. Let getFieldTypes( ) devuelve los tipos de campo para los que se puede utilizar la anotación.

  3. Deje que getPrinter() devuelva Impresora > para imprimir el valor del campo anotado.

  4. Deje que getParser() devuelva un Parser para analizar el clientValue para los campos de campo anotados.

La siguiente implementación de ejemplo de AnnotationFormatterFactory vincula el @NumberFormat anotación al formateador para que pueda especificar un estilo o patrón de numeración:

Java
la clase final pública NumberFormatAnnotationFormatterFactory implementa AnnotationFormatterFactory<NumberFormat> { conjunto público<Clase<?>> getFieldTypes() { return new HashSet<Class<?>>(asList(new Class<?>[] { Clase corta, clase entera, clase larga, clase flotante, clase doble, clase BigDecimal , BigInteger.clase })); } Impresora pública<Número> getPrinter(anotación NumberFormat, clase<?> tipo de campo) { return configureFormatterFrom(anotación, tipo de campo); } analizador público<Número> getParser(anotación NumberFormat, clase<?> tipo de campo) { return configureFormatterFrom(anotación, tipo de campo); } Formateador privado<Número> configureFormatterFrom(anotación NumberFormat, clase<?> tipo de campo) { if (!annotation.pattern().isEmpty()) { return new NumberStyleFormatter(annotation.pattern()); } else { Estilo estilo = anotación.estilo(); if (estilo == Estilo.PERCENT) { return new PercentStyleFormatter(); } else if (estilo == Estilo.CURRENCY) { return new CurrencyStyleFormatter(); } else { return nuevo NumberStyleFormatter(); } } } }
Kotlin
clase NumberFormatAnnotationFormatterFactory : AnnotationFormatterFactory<NumberFormat> { anular fun getFieldTypes(): Establecer<Class<*>> { return setOf(Short::class.java, Int::class.java, Long::class.java, Float::class.java, Double::class.java, BigDecimal::class.java, BigInteger::class .java) } anular fun getPrinter(anotación: NumberFormat, fieldType: Class<*>): Impresora<Number> { return configureFormatterFrom(annotation, fieldType) } anula fun getParser(annotation: NumberFormat, fieldType: Class<*>): Parser<Number> { return configureFormatterFrom(annotation, fieldType) } diversión privada configureFormatterFrom(annotation: NumberFormat, fieldType: Class<*>): Formateador<Number> { return if (annotation.pattern.isNotEmpty()) { NumberStyleFormatter(annotation.pattern) } else { val estilo = annotation.style cuando { estilo === NumberFormat.Style.PERCENT -> Estilo PercentStyleFormatter() === NumberFormat.Style.CURRENCY -> CurrencyStyleFormatter() más -> NumberStyleFormatter() } } } }

Para forzar el formato, puede anotar campos con @NumberFormat, como se muestra en el siguiente ejemplo:

Java
public class MyModel { @NumberFormat(style=Style.CURRENCY) decimal BigDecimal privado; }
Kotlin
class MyModel( @field:NumberFormat(style = Style.CURRENCY) valor privado decimal: BigDecimal )

API de anotación de formato

En el paquete org.springframework.format.annotation hay Portable API de anotación de formatos. Puede utilizar @NumberFormat para dar formato a campos Number como Double y Long, y @DateTimeFormat para formatear java.util.Date, java.util.Calendar, Long (para marcas de tiempo de milisegundos) y java .time de JSR-310.

El siguiente ejemplo formatea java.util.Date como una fecha ISO (aaaa-MM-dd) @DateTimeFormat se utiliza:

Java
clase pública MyModel { @DateTimeFormat(iso=ISO.DATE) Fecha privada fecha; }
Kotlin
clase MiModelo( @DateTimeFormat(iso=ISO.DATE) fecha de valor privado: Fecha) 

Interfaz SPI FormatterRegistry

FormatterRegistry es una interfaz SPI para registrar formateadores y conversores. FormattingConversionService es una implementación de FormatterRegistry adecuada para la mayoría de los entornos. Puede configurar esta opción mediante programación o declaración como un Spring Bean, por ejemplo usando FormattingConversionServiceFactoryBean. Debido a que esta implementación también implementa ConversionService, puede configurarlo directamente para usarlo con DataBinder de Spring y Spring Expression Language (SpEL).

De la siguiente manera: El listado muestra la interfaz SPI FormatterRegistry:

                paquete org.springframework.format; interfaz pública FormatterRegistry extiende
                    ConverterRegistry { void addPrinter(Impresora<?> impresora); void addParser(Parser<?>
                    analizador); void addFormatter(Formateador<?> formateador); void
                    addFormatterForFieldType(Class<?> fieldType, Formateador<?> formateador); void
                    addFormatterForFieldType(Class<?> tipo de campo, Impresora<?> impresora, Analizador<?>
                    analizador); void addFormatterForFieldAnnotation(AnnotationFormatterFactory<? extends Annotation>
                    annotationFormatterFactory); }

Como se desprende del listado anterior, puede registrar formateadores por tipo de campo o por anotación.

Interfaz SPI FormatterRegistry le permite configurar reglas de formato de forma centralizada, en lugar de duplicar dicha configuración para todos los controladores. Por ejemplo, puede forzar que todos los campos de fecha tengan un formato determinado o que los campos con una determinada anotación tengan un formato determinado. Con un FormatterRegistry genérico, usted define estas reglas una vez y se aplican siempre que sea necesario formatear.

Interfaz SPI FormatterRegistrar

FormatterRegistrar es una interfaz SPI para registrar formateadores y convertidores a través de FormatterRegistry. El siguiente listado muestra la definición de su interfaz:

paquete org.springframework.format; interfaz pública FormatterRegistrar { void RegisterFormatters (registro FormatterRegistry); }

FormatterRegistrar es útil cuando se registran múltiples convertidores y formateadores relacionados para una categoría específica de formato, como el formato de fecha. También puede resultar útil si el registro declarativo no es suficiente; por ejemplo, si el formateador necesita indexarse bajo un tipo de campo específico que no sea su propio <T>, o si una Impresora El par está registrado /Parser. La siguiente sección proporciona más información sobre cómo registrar convertidores y formateadores.

Configurar el formato en Spring MVC

Configurar el formato global de fecha y hora

Campos de fecha y hora predeterminados , no anotados con @DateTimeFormat, se convierten a partir de cadenas utilizando el estilo DateFormat.SHORT. Si lo desea, puede cambiar esto definiendo su propio formato global.

Para hacer esto, asegúrese de que Spring no registre formateadores predeterminados. En su lugar, registre formateadores manualmente con:

  • org.springframework.format.datetime.standard.DateTimeFormatterRegistrar

  • org.springframework.format.datetime.DateFormatterRegistrar

Por ejemplo, la siguiente configuración de Java registra un formato global de la forma aaaaMMdd:

Java
@Configuration public class AppConfig { @Bean public FormattingConversionService conversionService() { // Utilice DefaultFormattingConversionService, pero no registre el valor predeterminado valores DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false); // Asegúrate de que @NumberFormat todavía sea compatible conversionService.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory()); // Registre la conversión de fecha JSR-310 en un formato global específico DateTimeFormatterRegistrar registrador = new DateTimeFormatterRegistrar(); registrador.setDateFormatter(DateTimeFormatter.ofPattern("aaaaMMdd")); registrador.registerFormatters(servicio de conversión); // Registra la conversión de fecha en un formato global específico DateFormatterRegistrar registrador = new DateFormatterRegistrar(); registrador.setFormatter(new DateFormatter("aaaaMMdd")); registrador.registerFormatters(servicio de conversión); servicio de conversión de retorno; } }
Kotlin
@Configuration class AppConfig { @Bean fun conversionService(): FormattingConversionService { // Usar DefaultFormattingConversionService, pero no registre los valores predeterminados return DefaultFormattingConversionService(false).apply { // Asegúrese de que @NumberFormat todavía sea compatible addFormatterForFieldAnnotation(NumberFormatAnnotationFormatterFactory()) // Registre la conversión de fecha JSR-310 en un formato global específico val registrador = DateTimeFormatterRegistrar () registrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd")) registrar.registerFormatters(this) // Registrar la conversión de fecha en un formato global específico val registrar = DateFormatterRegistrar() registrar.setFormatter(DateFormatter("yyyyMMdd")) registrar.registerFormatters (this) } } }

Si prefiere la configuración basada en XML, puede utilizar FormattingConversionServiceFactoryBean. Lo siguiente El ejemplo muestra cómo hacer esto:

< ;?xml versión="1.0" codificación="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http:// www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd> <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean" > <property name="registerDefaultFormatters" value="false" /> <property name="formatters"> <set> <bean class="org.springframework.format.number.NumberFormatAnnotationFormatterFactory" /> ; </set> </property> <property name="formatterRegistrars"> <set> <bean class="org.springframework.format.datetime.standard.DateTimeFormatterRegistrar"> <property name= "dateFormatter"> <bean class="org.springframework.format.datetime.standard.DateTimeFormatterFactoryBean"> <property name="pattern" value="yyyyMMdd"/> </bean> </property> ; </bean> </set> </property> </frijol> </beans>

Tenga en cuenta que hay factores adicionales a considerar al configurar formatos de fecha y hora en aplicaciones web.