Spring Type Conversion
core.convert
包提供了一个通用类型转换系统。该系统定义了一个用于实现类型转换逻辑的 SPI 和一个用于在运行时执行类型转换的 API。在 Spring 容器中,你可以将此系统用作 PropertyEditor
实现的替代方案,以将外部化 Bean 属性值字符串转换为所需的属性类型。你还可以在你需要类型转换的应用程序中的任何地方使用公共 API。
The core.convert
package provides a general type conversion system. The system defines
an SPI to implement type conversion logic and an API to perform type conversions at
runtime. Within a Spring container, you can use this system as an alternative to
PropertyEditor
implementations to convert externalized bean property value strings to
the required property types. You can also use the public API anywhere in your application
where type conversion is needed.
Converter SPI
用于实现类型转换逻辑的 SPI 非常简单且类型化,如下面的接口定义所示:
The SPI to implement type conversion logic is simple and strongly typed, as the following interface definition shows:
public interface Converter<S, T> {
T convert(S source);
}
要创建你自己的转换器,请实现 Converter
接口,并使用你正在从中转换的类型参数化 S
,并用你正在转换到的类型参数化 T
。你还可以透明地应用此类转换器,如果 S
的集合或数组需要转换为 T
的数组或集合,前提是委托数组或集合转换器也已注册(DefaultConversionService
默认执行此操作)。
To create your own converter, implement the Converter
interface and parameterize S
as the type you are converting from and T
as the type you are converting to. You can also transparently apply such a
converter if a collection or array of S
needs to be
converted to an array or collection of T
, provided that a delegating array or collection
converter has been registered as well (which DefaultConversionService
does by default).
对于每次调用 convert(S)
,保证源参数不为 null。如果转换失败,你的 Converter
可能会抛出任何未经检查的异常。具体来说,它应该抛出 IllegalArgumentException
来报告无效的源值。注意确保你的 Converter
实现是线程安全的。
For each call to convert(S)
, the source argument is guaranteed to not be null. Your
Converter
may throw any unchecked exception if conversion fails. Specifically, it should throw an
IllegalArgumentException
to report an invalid source value.
Take care to ensure that your Converter
implementation is thread-safe.
core.convert.support
包中提供了多种转换器实现以供使用。其中包括从字符串到数字和其他常见类型的转换器。以下清单显示了 StringToInteger
类,这是一个典型的 Converter
实现:
Several converter implementations are provided in the core.convert.support
package as
a 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:
final class StringToInteger implements Converter<String, Integer> {
public Integer convert(String source) {
return Integer.valueOf(source);
}
}
Using ConverterFactory
当需要集中转换整个类层次结构的逻辑时(例如,从 String
转换为 Enum
对象时),你可以实现 ConverterFactory
,如下面的示例所示:
When you need to centralize the conversion logic for an entire class hierarchy
(for example, when converting from String
to Enum
objects), you can implement
ConverterFactory
, as the following example shows:
public interface ConverterFactory<S, R> {
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
将 S 参数化为你正在从中转换的类型,将 R 参数化为定义你可以转换为的类的 范围 的基类型。然后实现 getConverter(Class<T>)
,其中 T 是 R 的子类。
Parameterize S to be the type you are converting from and R to be the base type defining
the range of classes you can convert to. Then implement getConverter(Class<T>)
,
where T is a subclass of R.
以 StringToEnumConverterFactory
为例:
Consider the StringToEnumConverterFactory
as an example:
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
当你需要一个复杂的 Converter
实现时,请考虑使用 GenericConverter
接口。GenericConverter
具有比 Converter
更灵活但类型化程度较差的签名,支持在多个源和目标类型之间进行转换。此外,GenericConverter
提供了源和目标字段上下文,你可以在实现转换逻辑时使用它。此类上下文允许类型转换由字段注释或在字段签名上声明的泛型信息驱动。以下清单显示了 GenericConverter
的接口定义:
When you require a sophisticated Converter
implementation, consider using the
GenericConverter
interface. With a more flexible but less strongly typed signature
than Converter
, a GenericConverter
supports converting between multiple source and
target types. In addition, a GenericConverter
makes available source and target field
context that you can use when you implement your conversion logic. Such context lets a
type conversion be driven by a field annotation or by generic information declared on a
field signature. The following listing shows the interface definition of GenericConverter
:
public interface GenericConverter {
public Set<ConvertiblePair> getConvertibleTypes();
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}
要实现 GenericConverter
,让 getConvertibleTypes()
返回支持的源→目标类型对。然后实现 convert(Object, TypeDescriptor, TypeDescriptor)
来包含你的转换逻辑。源 TypeDescriptor
提供对包含要转换的值的源字段的访问。目标 TypeDescriptor
提供对将转换后的值设置到的目标字段的访问。
To implement a GenericConverter
, have getConvertibleTypes()
return the supported
source→target type pairs. Then implement convert(Object, TypeDescriptor,
TypeDescriptor)
to contain your conversion logic. The source TypeDescriptor
provides
access to the source field that holds the value being converted. The target TypeDescriptor
provides access to the target field where the converted value is to be set.
GenericConverter
的一个好例子是可以在 Java 数组和集合之间进行转换的转换器。此类 ArrayToCollectionConverter
内省声明目标集合类型的字段,以解析集合的元素类型。这允许在将集合设置到目标字段之前将源数组中的每个元素转换为集合元素类型。
A good example of a GenericConverter
is a converter that converts between a Java array
and a collection. Such an ArrayToCollectionConverter
introspects the field that declares
the target collection type to resolve the collection’s element type. This lets each
element in the source array be converted to the collection element type before the
collection is set on the target field.
因为 |
Because |
Using ConditionalGenericConverter
有时,你希望只有在特定条件成立时才运行 Converter
。例如,你可能希望只有在目标字段上存在特定注释时才运行 Converter
,或者你可能希望只有在目标类上定义特定方法(例如 static valueOf
方法)时才运行 Converter
。ConditionalGenericConverter
是 GenericConverter
和 ConditionalConverter
接口的并集,允许你定义此类自定义匹配条件:
Sometimes, you want a Converter
to run only if a specific condition holds true. For
example, you might want to run a Converter
only if a specific annotation is present
on the target field, or you might want to run a Converter
only if a specific method
(such as a static valueOf
method) is defined on the target class.
ConditionalGenericConverter
is the union of the GenericConverter
and
ConditionalConverter
interfaces that lets you define such custom matching criteria:
public interface ConditionalConverter {
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}
ConditionalGenericConverter
的一个好例子是 IdToEntityConverter
,它可以在持久性实体标识符和实体引用之间进行转换。此类 IdToEntityConverter
仅当目标实体类型声明静态查找器方法(例如,findAccount(Long)
)时才匹配。你可以在 matches(TypeDescriptor, TypeDescriptor)
的实现中执行此类查找器方法检查。
A good example of a ConditionalGenericConverter
is an IdToEntityConverter
that converts
between a persistent entity identifier and an entity reference. Such an IdToEntityConverter
might match only if the target entity type declares a static finder method (for example,
findAccount(Long)
). You might perform such a finder method check in the implementation of
matches(TypeDescriptor, TypeDescriptor)
.
The ConversionService
API
ConversionService
定义了一个统一的 API,用于在运行时执行类型转换逻辑。转换器通常在以下 facades 接口后面运行:
ConversionService
defines a unified API for executing type conversion logic at
runtime. Converters are often run behind the following facade interface:
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);
}
大多数 ConversionService
实现也实现了 ConverterRegistry
,它提供了用于注册转换器的 SPI。在内部,ConversionService
实现委托给其注册的转换器来执行类型转换逻辑。
Most ConversionService
implementations also implement ConverterRegistry
, which
provides an SPI for registering converters. Internally, a ConversionService
implementation delegates to its registered converters to carry out type conversion logic.
core.convert.support
包中提供了一个稳健的 ConversionService
实现。GenericConversionService
是通用的实现,适合在大多数环境中使用。ConversionServiceFactory
提供了一个便于创建常见 ConversionService
配置的工厂。
A robust ConversionService
implementation is provided in the core.convert.support
package. GenericConversionService
is the general-purpose implementation suitable for
use in most environments. ConversionServiceFactory
provides a convenient factory for
creating common ConversionService
configurations.
Configuring a ConversionService
ConversionService
是一个无状态对象,设计为在应用程序启动时实例化,然后在多个线程之间共享。在 Spring 应用程序中,通常为每个 Spring 容器(或 ApplicationContext
)配置一个 ConversionService
实例。Spring 会获取该 ConversionService
,并在框架需要执行类型转换时使用它。您还可以在任何 bean 中注入此 ConversionService
并直接调用它。
A ConversionService
is a stateless object designed to be instantiated at application
startup and then shared between multiple threads. In a Spring application, you typically
configure a ConversionService
instance for each Spring container (or ApplicationContext
).
Spring picks up that ConversionService
and uses it whenever a type
conversion needs to be performed by the framework. You can also inject this
ConversionService
into any of your beans and invoke it directly.
如果没有向 Spring 注册 |
If no |
要使用 Spring 注册默认的 ConversionService
,请添加以下具有 id
为 conversionService
的 bean 定义:
To register a default ConversionService
with Spring, add the following bean definition
with an id
of conversionService
:
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean"/>
默认的 ConversionService
可以在字符串、数字、枚举、集合、映射和其他常见类型之间进行转换。要使用您自己的自定义转换器补充或覆盖默认转换器,请设置 converters
属性。属性值可以实现任何 Converter
、ConverterFactory
或 GenericConverter
接口。
A default ConversionService
can convert between strings, numbers, enums, collections,
maps, and other common types. To supplement or override 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>
在 Spring MVC 应用程序中使用 `ConversionService`也很常见。请参阅 Spring MVC 章节中的Conversion and Formatting。
It is also common to use a ConversionService
within a Spring MVC application. See
Conversion and Formatting in the Spring MVC chapter.
在某些情况下,你可能希望在转换期间应用格式。有关使用 FormattingConversionServiceFactoryBean
的详细信息,请参阅 The FormatterRegistry
SPI。
In certain situations, you may wish to apply formatting during conversion. See
The FormatterRegistry
SPI for details on using FormattingConversionServiceFactoryBean
.
Using a ConversionService
Programmatically
要以编程方式使用 ConversionService
实例,您可以注入对它的引用,就像对任何其他 bean 所做的那样。以下示例展示了如何执行此操作:
To work with a ConversionService
instance programmatically, you can inject a reference to
it like you would for any other bean. The following example shows how to do so:
-
Java
-
Kotlin
@Service
public class MyService {
private final ConversionService conversionService;
public MyService(ConversionService conversionService) {
this.conversionService = conversionService;
}
public void doIt() {
this.conversionService.convert(...)
}
}
@Service
class MyService(private val conversionService: ConversionService) {
fun doIt() {
conversionService.convert(...)
}
}
对于大多数用例,可以使用指定 targetType
的 convert
方法,但它不适用于更复杂的类型,例如参数化元素的集合。例如,如果您想以编程方式将 Integer
的 List
转换为 String
的 List
,则需要提供源类型和目标类型的正式定义。
For most use cases, you can use the convert
method that specifies the targetType
, but it
does not work with more complex types, such as a collection of a parameterized element.
For example, if you want to convert a List
of Integer
to a List
of String
programmatically,
you need to provide a formal definition of the source and target types.
幸运的是,TypeDescriptor
提供了各种选项来简化此操作,如下面的示例所示:
Fortunately, TypeDescriptor
provides various options to make doing so straightforward,
as the following example shows:
-
Java
-
Kotlin
DefaultConversionService cs = new DefaultConversionService();
List<Integer> input = ...
cs.convert(input,
TypeDescriptor.forObject(input), // List<Integer> type descriptor
TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)));
val cs = DefaultConversionService()
val input: List<Integer> = ...
cs.convert(input,
TypeDescriptor.forObject(input), // List<Integer> type descriptor
TypeDescriptor.collection(List::class.java, TypeDescriptor.valueOf(String::class.java)))
请注意,DefaultConversionService
自动注册了适合大多数环境的转换器。这包括集合转换器、标量转换器和基本的 Object
到 String
的转换器。您可以使用 DefaultConversionService
类上的静态 addDefaultConverters
方法将相同的转换器注册到任何 ConverterRegistry
中。
Note that DefaultConversionService
automatically registers converters that are
appropriate for most environments. This includes collection converters, scalar
converters, and basic Object
-to-String
converters. You can register the same converters
with any ConverterRegistry
by using the static addDefaultConverters
method on the DefaultConversionService
class.
值类型的转换器可用于数组和集合,因此无需创建特定的转换器来从 S
的 Collection
转换为 T
的 Collection
,假设标准集合处理是适当的。
Converters for value types are reused for arrays and collections, so there is
no need to create a specific converter to convert from a Collection
of S
to a
Collection
of T
, assuming that standard collection handling is appropriate.