Spring 3 introduced the core.convert package, which provides a general type conversion system. The system defines an SPI (Service Provider Interface) to implement type conversion logic and an API to perform type conversion at runtime. In a Spring container, you can use this system as an alternative to the PropertyEditor implementation to convert extracted bean property value strings into the required property types. You can also use the public API anywhere in your application where type conversion is required.

SPI Converter

The SPI interface for implementing type conversion logic is simple and strongly typed , as shown in the following interface definition:


package org.springframework.core.convert.converter;
public interface Converter<S, T> {
    T convert(S source);
}

To create your own converter, implement the Converter interface and parameterize S as the type from which you are converting to, and T as the type you are converting to. You can also use such a mapper in a clear way if the collection or array S needs to be converted to an array or collection T, provided that the delegating array or collection mapper is also registered (that DefaultConversionService does the default).

For each call to convert(S) the source argument is guaranteed not to be null. Your Converter can throw any unchecked exception if the conversion fails. In particular, it must throw an IllegalArgumentException exception to indicate that the source value is illegal. Be sure that your Converter implementation is thread safe.

Several converter implementations are provided in the core.convert.support package for convenience. These include converters from strings to numbers and other common types. The following listing shows the StringToInteger class, which is a typical Converter implementation:


package org.springframework.core.convert.support;
final class StringToInteger implements Converter<String, Integer> {
    public Integer convert(String source) {
        return Integer.valueOf(source);
    }
}

Using ConverterFactory

If you need to centralize the conversion logic for the entire class hierarchy (for example, when converting from String objects to Enum objects), you can implement a ConverterFactory, as shown in the following example:


package org.springframework.core.convert.converter;
public interface ConverterFactory<S, R> {
    <T extends R> Converter<S, T> getConverter(Class<T> targetType);
}

Parameterize S as the type you are converting from, and R as the base type defining the range of classes you are converting to you can convert. Then implement getConverter(Class<T>)), where T is a subclass of R.

As an example, consider 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());
        }
    }
}

Using GenericConverter

If you need a complex implementation of Converter, use the GenericConverter interface. GenericConverter, with a more flexible but less strongly typed signature than Converter, supports conversion between multiple source and target types. In addition, GenericConverter makes available source and target field context that can be used when implementing conversion logic. This context allows type conversion based on a field's annotation or general information declared in the field's signature. The following listing shows the definition of the GenericConverter interface:


package org.springframework.core.convert.converter;
public interface GenericConverter {
    public Set<ConvertiblePair> getConvertibleTypes();
    Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}

The GenericConverter implementation requires the getConvertibleTypes() function to return supported source type pairs→ target. Then implement convert(Object, TypeDescriptor, TypeDescriptor) to add conversion logic. The source TypeDescriptor provides access to the source field containing the value to be converted. The target's TypeDescriptor provides access to the target field to which the converted value is to be set.

A good example of a GenericConverter is a converter that converts a Java array to a collection. Such an ArrayToCollectionConverter inspects the field declaring the type of the target collection to determine the type of the collection element. This allows each element of the source array to be converted to the collection element type before the collection is set to the target field.

Because GenericConverter is a more complex SPI interface and should only be used when necessary. For basic type conversion needs, use Converter or ConverterFactory.

Using ConditionalGenericConverter

Sometimes required so that Converter is launched only when a certain condition is met. For example, you might want to run Converter only if a certain annotation is present on the target field, or run Converter only if the target class has a specific method defined (for example, the method static valueOf). ConditionalGenericConverter is a combination of the GenericConverter and ConditionalConverter interfaces, allowing you to define the following special matching criteria:


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

A good example of a ConditionalGenericConverter is IdToEntityConverter, which converts between a persistent entity identifier and an entity reference. Such an IdToEntityConverter may only be suitable if the target entity type declares a static lookup method (for example, findAccount(Long)). You can perform such a search method check in the implementation of matches(TypeDescriptor, TypeDescriptor).

API ConversionService

ConversionService defines a unified API for performing type conversion logic during program execution. Transformers often start executing after the following facade interface:

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

Most implementations of ConversionService also implement ConverterRegistry, which provides an SPI interface for registering converters. Internally, the ConversionService implementation delegates to its registered converters to perform type conversion logic.

A robust ConversionService implementation is provided in the core.convert.support. GenericConversionService is a general purpose implementation suitable for use in most environments. ConversionServiceFactory provides a convenient factory for creating common ConversionService configurations.

Configuring ConversionService

ConversionService is a stateless object that is instantiated when an application starts and is subsequently shared among multiple threads. In a Spring application, an instance of ConversionService is typically configured for each Spring container (or ApplicationContext). Spring defines this ConversionService and uses it whenever the framework needs to perform a type conversion. You can also inject this ConversionService into any of your beans and call it directly.

If is not registered with Spring ConversionService, then the original PropertyEditor based system is used.

To register a standard ConversionService in Spring, add the following bean definition specifying id as conversionService:


<bean id="conversionService"
    class="org.springframework.context.support.ConversionServiceFactoryBean"/>

The standard ConversionService can convert strings, numbers, enum types, collections, associative arrays, and other common types. To supplement or replace the default converters with your own custom converters, set the converters property. Property values can implement any of the Converter, ConverterFactory, or GenericConverter interfaces.


<bean id="conversionService"
      class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="example.MyCustomConverter"/>
        </set>
    </property>
</bean>

Also, ConversionService is often used in Spring MVC applications.

In some situations, you can apply formatting during conversion.

Using ConversionService Programmatically

To work with a ConversionService instance programmatically, you can embed a reference on it, as is done for any other bean. The following example shows how to do this:

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

For most use cases You can use the convert method to specify a targetType, but it won't work with more complex types, such as a collection of a parameterized element. For example, if you need to programmatically convert a List of type Integer to a List of type String, you must provide a formal definition of the original and target types.

Fortunately, the TypeDescriptor provides various options that make this as easy as the following example:

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)))

Note that DefaultConversionService automatically registers converters that are suitable for most environments. These include collection converters, scalar converters, and basic Object-to-String converters. You can register the same converters in any ConverterRegistry using the static addDefaultConverters method code> class DefaultConversionService.

Converters for value types are reused for arrays and collections, so there is no need to create a specialized converter to convert from a Collection type S in Collection of type T, assuming that standard collection handling would be appropriate.