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.

Porque 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.

Si no está registrado con Spring 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:

Java

@Service
public class MyService {
    public MyService(ConversionService conversionService) {
        this.conversionService = conversionService;
    }
    public void doIt() {
        this.conversionService.convert(...)
    }
}
Kotlin

@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:

Java

DefaultConversionService cs = new DefaultConversionService();
List<Integer> input = ...
cs.convert(input,
    TypeDescriptor.forObject(input), c List<Integer>
    TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)));
Kotlin

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.