Spring 3 introdujo el paquete core.convert
, que proporciona un sistema de conversión de tipos general.
El sistema define una SPI (interfaz de proveedor de servicios) para implementar la lógica de conversión de tipos y
una API para realizar la conversión de tipos en tiempo de ejecución. En un contenedor Spring, puede utilizar este
sistema como alternativa a la implementación PropertyEditor
para convertir cadenas de valores de
propiedad extraídas del bean en los tipos de propiedad requeridos. También puede utilizar la API pública en
cualquier lugar de su aplicación donde se requiera conversión de tipos.
Convertidor SPI
La interfaz SPI para implementar la lógica de conversión de tipos es simple y fuertemente tipada, como se muestra en la siguiente definición de interfaz:
package org.springframework.core.convert.converter;
public interface Converter<S, T> {
T convert(S source);
}
Para crear su propio convertidor, implemente la interfaz Converter
y parametrice S
como tipo. Desde el que está convirtiendo y T
como el tipo al que está convirtiendo. También puede
utilizar un asignador de este tipo de forma clara si la colección o matriz S
necesita convertirse en
una matriz o colección T
, siempre que la matriz o el asignador de colección delegados también está
registrado (ese DefaultConversionService
es el predeterminado).
Para cada llamada a convert(S)
se garantiza que el argumento fuente no será nulo. Su Convertidor
puede generar cualquier excepción no
marcada si la conversión falla. En particular, debe generar una excepción IllegalArgumentException
para
indicar que el valor fuente es ilegal. Asegúrese de que su implementación de Converter
sea segura para
subprocesos.
Se proporcionan varias implementaciones de convertidor en el paquete
core.convert.support
para mayor comodidad. Estos incluyen convertidores de cadenas a números y otros
tipos comunes. La siguiente lista muestra la clase StringToInteger
, que es una implementación típica de
Converter
:
package org.springframework.core.convert.support;
final class StringToInteger implements Converter<String, Integer> {
public Integer convert(String source) {
return Integer.valueOf(source);
}
}
Usando ConverterFactory
Si necesita centralizar la lógica de conversión para toda la
jerarquía de clases (por ejemplo, al convertir de objetos String
a objetos Enum
), puede
implementar un ConverterFactory
, como se muestra en el siguiente ejemplo:
package org.springframework.core.convert.converter;
public interface ConverterFactory<S, R> {
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
Parametrice S como el tipo desde el que está convirtiendo y R como el tipo base que define el
rango de clases que está convirtiendo a usted puede convertir. Luego implemente getConverter(Class<T>)
),
donde T es una subclase de R.
Como ejemplo, considere StringToEnumConverterFactory
:
package org.springframework.core.convert.support;
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToEnumConverter(targetType);
}
private final class StringToEnumConverter<T extends Enum> implements Converter<String, T> {
private Class<T> enumType;
public StringToEnumConverter(Class<T> enumType) {
this.enumType = enumType;
}
public T convert(String source) {
return (T) Enum.valueOf(this.enumType, source.trim());
}
}
}
Usando GenericConverter
Si necesita una implementación compleja de
Converter
, utilice la interfaz GenericConverter
. GenericConverter
, con una
firma más
flexible pero menos tipificada que Converter
, admite la conversión entre múltiples tipos de origen y
destino. Además, GenericConverter
pone a disposición el contexto de los campos de origen y destino que
se puede utilizar al implementar la lógica de conversión. Este contexto permite la conversión de tipos basada en la
anotación de un campo o en la información general declarada en la firma del campo. El siguiente listado muestra la
definición de la interfaz GenericConverter
:
package org.springframework.core.convert.converter;
public interface GenericConverter {
public Set<ConvertiblePair> getConvertibleTypes();
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}
La implementación de GenericConverter
requiere la función getConvertibleTypes()
para
devolver pares de tipos de fuente admitidos→ objetivo. Luego implemente convert(Object, TypeDescriptor,
TypeDescriptor)
para agregar lógica de conversión. El TypeDescriptor
de origen proporciona
acceso al campo de origen que contiene el valor que se va a convertir. El TypeDescriptor
del destino
proporciona acceso al campo de destino en el que se establecerá el valor convertido.
Un buen ejemplo de un
GenericConverter
es un convertidor que convierte una matriz Java en una colección. Un ArrayToCollectionConverter
de este tipo inspecciona el campo que declara el tipo de colección de destino para determinar el tipo de elemento de
colección. Esto permite que cada elemento de la matriz de origen se convierta al tipo de elemento de la colección
antes de que la colección se establezca en el campo de destino.
GenericConverter
es una interfaz SPI más compleja y solo
debe usarse cuando sea necesario. Para necesidades básicas de conversión de tipos, utilice Converter
o
ConverterFactory
.
Uso de ConditionalGenericConverter
A veces es necesario para que Converter
se inicie solo cuando se cumpla una determinada condición. Por ejemplo, es posible que desee ejecutar Converter
solo si una determinada anotación está presente en el campo de destino, o ejecutar Converter
solo si la
clase de destino tiene un método específico definido (por ejemplo). Por ejemplo, el method static valueOf
). ConditionalGenericConverter
es una combinación de las interfaces
GenericConverter
y ConditionalConverter
, lo que le permite definir los siguientes
criterios de coincidencia especiales:
public interface ConditionalConverter {
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
} public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}
Un buen ejemplo de un ConditionalGenericConverter
es IdToEntityConverter
, que
convierte entre un identificador de entidad persistente y una referencia de entidad. Un
IdToEntityConverter
solo puede ser adecuado si el tipo de entidad de destino declara un método de
búsqueda estático (por ejemplo, findAccount(Long)
). Puede realizar una verificación de método de
búsqueda de este tipo en la implementación de matches(TypeDescriptor, TypeDescriptor)
.
API
ConversionService
ConversionService
define una API unificada para realizar la
lógica de conversión de tipos durante la ejecución del programa. Los transformadores a menudo comienzan a ejecutarse
después de la siguiente interfaz de fachada:
package org.springframework.core.convert;
public interface ConversionService {
boolean canConvert(Class<?> sourceType, Class<?> targetType);
<T> T convert(Object source, Class<T> targetType);
boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}
La mayoría de las implementaciones de ConversionService
también implementan ConverterRegistry
,
que proporciona una interfaz SPI para registrar convertidores. . Internamente, la implementación de ConversionService
delega a sus convertidores registrados la realización de la lógica de conversión de tipos.
Se proporciona una
implementación sólida de ConversionService
en el core.convert.support
paquete de.
GenericConversionService
es una implementación de propósito general adecuada para su uso en la
mayoría de los entornos. ConversionServiceFactory
proporciona una fábrica conveniente para crear
configuraciones comunes de ConversionService
.
Configuración de
ConversionService
ConversionService
es un objeto sin estado del que se crea una
instancia cuando se inicia una aplicación y posteriormente se comparte entre varios subprocesos. En una aplicación
Spring, normalmente se configura una instancia de ConversionService
para cada contenedor Spring (o
ApplicationContext
). Spring define este ConversionService
y lo usa siempre que el marco
necesita realizar una conversión de tipo. También puedes inyectar este ConversionService
en cualquiera
de tus beans y llamarlo directamente.
ConversionService
, entonces se
utiliza el sistema original basado en PropertyEditor
.
Para registrar un ConversionService
estándar en Spring, agregue el siguiente bean definición que
especifica id
como conversionService
:
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean"/>
El ConversionService
estándar puede convertir cadenas, números, tipos de enumeración,
colecciones, matrices asociativas y otros tipos comunes. Para complementar o reemplazar los convertidores
predeterminados con sus propios convertidores personalizados, establezca la propiedad converters
. Los
valores de propiedad pueden implementar cualquiera de las interfaces Converter
,
ConverterFactory
o GenericConverter
.
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="example.MyCustomConverter"/>
</set>
</property>
</bean>
Además, ConversionService
se usa a menudo en aplicaciones Spring MVC.
En En algunas situaciones, puede aplicar formato durante la conversión.
Uso de ConversionService
mediante
programación
Para trabajar con una instancia de ConversionService
mediante programación, debe
Puede incrustar una referencia en él, como se hace con cualquier otro bean. El siguiente ejemplo muestra cómo hacer
esto:
@Service
public class MyService {
public MyService(ConversionService conversionService) {
this.conversionService = conversionService;
}
public void doIt() {
this.conversionService.convert(...)
}
}
@Service
class MyService(private val conversionService: ConversionService) {
fun doIt() {
conversionService.convert(...)
}
}
Para la mayoría de los casos de uso Puede utilizar el método convert
para especificar un targetType
,
pero no funcionará con tipos más complejos, como una colección de un elemento parametrizado. Por ejemplo, si
necesita convertir mediante programación una List
de tipo Integer
en una
List
de tipo String
, debe debe proporcionar una definición formal de los tipos original y
de destino.
Afortunadamente, el TypeDescriptor
proporciona varias opciones que hacen esto tan
fácil como el siguiente ejemplo:
DefaultConversionService cs = new DefaultConversionService();
List<Integer> input = ...
cs.convert(input,
TypeDescriptor.forObject(input), c List<Integer>
TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)));
val cs = DefaultConversionService()
val input: List<Integer> = ...
cs.convert(input,
TypeDescriptor.forObject(input), // Type descriptor List<Integer>
TypeDescriptor.collection(List::class.java, TypeDescriptor.valueOf(String::class.java)))
Tenga en cuenta que DefaultConversionService
registra automáticamente los convertidores que son
adecuados para la mayoría de los entornos. Estos incluyen convertidores de colección, convertidores escalares y
convertidores Object-to-String
básicos. Puede registrar los mismos convertidores en cualquier ConverterRegistry
utilizando el método estático addDefaultConverters
código de método clase DefaultConversionService
.
Los convertidores para tipos de valores se reutilizan para matrices y colecciones, por lo que no es necesario
crear un convertidor especializado para convertir desde una Collection
escriba S
en
Collection
de tipo T
, asumiendo que el manejo estándar de la colección sería apropiado.