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.
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.
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:
@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(...)
}
}
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:
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)))
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.
GO TO FULL VERSION