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.