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
Long
. 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
:
package org.springframework.format;
public interface Formatter<T> extends Printer<T>, Parser<T> {
}
Formatter
se extiende desde el bloque de construcción Printer
y
Parser
interfaces. El siguiente listado muestra las definiciones de estas dos interfaces:
public interface Printer<T> {
String print(T fieldValue, Locale locale);
}
import java.text.ParseException;
public interface Parser<T> {
T parse(String clientValue, Locale locale) throws 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
:
package org.springframework.format.datetime;
public final class DateFormatter implements Formatter<Date> {
private String pattern;
public DateFormatter(String pattern) {
this.pattern = pattern;
}
public String print(Date date, Locale locale) {
if (date == null) {
return "";
}
return getDateFormat(locale).format(date);
}
public Date parse(String formatted, Locale locale) throws ParseException {
if (formatted.length() == 0) {
return null;
}
return getDateFormat(locale).parse(formatted);
}
protected DateFormat getDateFormat(Locale locale) {
DateFormat dateFormat = new SimpleDateFormat(this.pattern, locale);
dateFormat.setLenient(false);
return dateFormat;
}
}
class DateFormatter(private val pattern: String) : Formatter<Date> {
override fun print(date: Date, locale: Locale)
= getDateFormat(locale).format(date)
@Throws(ParseException::class)
override fun parse(formatted: String, locale: Locale)
= getDateFormat(locale).parse(formatted)
protected fun 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
:
package org.springframework.format;
public interface AnnotationFormatterFactory<A extends Annotation> {
Set<Class<?>> getFieldTypes();
Printer<?> getPrinter(A annotation, Class<?> fieldType);
Parser<?> getParser(A annotation, Class<?> fieldType);
}
Para crear una implementación:
Parametrizar A como campos
annotationType
, con el que desea asociar la lógica de formato, por ejemplo,org.springframework.format.annotation.DateTimeFormat
.Let
getFieldTypes()
devuelve los tipos de campo para los que se puede utilizar la anotación.Deje que
getPrinter()
devuelvaPrinter
para imprimir el valor del campo anotado.Deje que
getParser()
devuelva unParser
para analizar elclientValue
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:
public final class NumberFormatAnnotationFormatterFactory
implements AnnotationFormatterFactory<NumberFormat> {
public Set<Class<?>> getFieldTypes() {
return new HashSet<Class<?>>(asList(new Class<?>[] {
Short.class, Integer.class, Long.class, Float.class,
Double.class, BigDecimal.class, BigInteger.class }));
}
public Printer<Number> getPrinter(NumberFormat annotation, Class<?> fieldType) {
return configureFormatterFrom(annotation, fieldType);
}
public Parser<Number> getParser(NumberFormat annotation, Class<?> fieldType) {
return configureFormatterFrom(annotation, fieldType);
}
private Formatter<Number> configureFormatterFrom(NumberFormat annotation, Class<?> fieldType) {
if (!annotation.pattern().isEmpty()) {
return new NumberStyleFormatter(annotation.pattern());
} else {
Style style = annotation.style();
if (style == Style.PERCENT) {
return new PercentStyleFormatter();
} else if (style == Style.CURRENCY) {
return new CurrencyStyleFormatter();
} else {
return new NumberStyleFormatter();
}
}
}
}
class NumberFormatAnnotationFormatterFactory : AnnotationFormatterFactory<NumberFormat> {
override fun getFieldTypes(): Set<Class<*>> {
return setOf(Short::class.java, Int::class.java, Long::class.java, Float::class.java, Double::class.java, BigDecimal::class.java, BigInteger::class.java)
}
override fun getPrinter(annotation: NumberFormat, fieldType: Class<*>): Printer<Number> {
return configureFormatterFrom(annotation, fieldType)
}
override fun getParser(annotation: NumberFormat, fieldType: Class<*>): Parser<Number> {
return configureFormatterFrom(annotation, fieldType)
}
private fun configureFormatterFrom(annotation: NumberFormat, fieldType: Class<*>): Formatter<Number> {
return if (annotation.pattern.isNotEmpty()) {
NumberStyleFormatter(annotation.pattern)
} else {
val style = annotation.style
when {
style === NumberFormat.Style.PERCENT -> PercentStyleFormatter()
style === NumberFormat.Style.CURRENCY -> CurrencyStyleFormatter()
else -> NumberStyleFormatter()
}
}
}
}
Para forzar el formato, puede anotar campos con @NumberFormat, como se muestra en el siguiente ejemplo:
public class MyModel {
@NumberFormat(style=Style.CURRENCY)
private BigDecimal decimal;
}
class MyModel(
@field:NumberFormat(style = Style.CURRENCY) private val 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:
public class MyModel {
@DateTimeFormat(iso=ISO.DATE)
private Date date;
}
class MyModel(
@DateTimeFormat(iso=ISO.DATE) private val date: Date
)
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
:
package org.springframework.format;
public interface FormatterRegistry extends ConverterRegistry {
void addPrinter(Printer printer);
void addParser(Parser parser);
void addFormatter(Formatter formatter);
void addFormatterForFieldType(Class fieldType, Formatter formatter);
void addFormatterForFieldType(Class fieldType, Printer printer, Parser parser);
void addFormatterForFieldAnnotation(AnnotationFormatterFactory 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:
package org.springframework.format;
public interface FormatterRegistrar {
void registerFormatters(FormatterRegistry registry);
}
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 Printer
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 yyyyMMdd
:
@Configuration
public class AppConfig {
@Bean
public FormattingConversionService conversionService() {
// Use DefaultFormattingConversionService, but don't register default values
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false);
// Make sure @NumberFormat is still supported
conversionService.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
// Register the JSR-310 date conversion in a specific global format
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd"));
registrar.registerFormatters(conversionService);
// Register the date conversion in a specific global format
DateFormatterRegistrar registrar = new DateFormatterRegistrar();
registrar.setFormatter(new DateFormatter("yyyyMMdd"));
registrar.registerFormatters(conversionService);
return conversionService;
}
}
@Configuration
class AppConfig {
@Bean
fun conversionService(): FormattingConversionService {
// Use DefaultFormattingConversionService, but don't register default values
return DefaultFormattingConversionService(false).apply {
// Make sure @NumberFormat is still supported
addFormatterForFieldAnnotation(NumberFormatAnnotationFormatterFactory())
// Register JSR-310 date conversion in a specific global format
val registrar = DateTimeFormatterRegistrar ()
registrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd"))
registrar.registerFormatters(this)
// Register the date conversion in a specific global format
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:
<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>
</bean>
</beans>
Tenga en cuenta que hay factores adicionales a considerar al configurar formatos de fecha y hora en aplicaciones web.
GO TO FULL VERSION