Spring 类型转换
core.convert 包提供了一个通用的类型转换系统。该系统定义了
一个 SPI 来实现类型转换逻辑,以及一个 API 来在运行时执行类型转换。
在 Spring 容器中,你可以使用这个系统作为 PropertyEditor 实现的替代方案,
将外部化的 bean 属性值字符串转换为所需的属性类型。
你也可以在应用程序中任何需要类型转换的地方使用公共 API。
Converter SPI
实现类型转换逻辑的 SPI 简单且强类型,如以下 接口定义所示:
package org.springframework.core.convert.converter;
public interface Converter<S, T> {
T convert(S source);
}
要创建你自己的转换器,请实现 Converter 接口,并将 S 参数化为你要从中转换的类型,
将 T 参数化为你要转换到的类型。
如果 S 的集合或数组需要转换为 T 的数组或集合,
并且已注册了委派数组或集合转换器(DefaultConversionService 默认会这样做),
你也可以透明地应用这样的转换器。
对于每次对 convert(S) 的调用,源参数保证不为 null。
如果转换失败,你的 Converter 可以抛出任何未检查的异常。
具体来说,它应该抛出 IllegalArgumentException 来报告无效的源值。
请注意确保你的 Converter 实现是线程安全的。
core.convert.support 包中提供了几个转换器实现以方便使用。
这些包括从字符串到数字和其他常见类型的转换器。
以下列表显示了 StringToInteger 类,它是一个典型的 Converter 实现:
package org.springframework.core.convert.support;
final class StringToInteger implements Converter<String, Integer> {
public Integer convert(String source) {
return Integer.valueOf(source);
}
}
使用 ConverterFactory
当你需要集中处理整个类层次结构的转换逻辑时
(例如,当从 String 转换为 Enum 对象时),你可以实现
ConverterFactory,如以下示例所示:
package org.springframework.core.convert.converter;
public interface ConverterFactory<S, R> {
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
将 S 参数化为你要从中转换的类型,将 R 参数化为定义你可以转换到的类 范围 的基类型。
然后实现 getConverter(Class<T>),其中 T 是 R 的子类。
以 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());
}
}
}
使用 GenericConverter
当你需要更复杂的 Converter 实现时,请考虑使用
GenericConverter 接口。GenericConverter 具有比
Converter 更灵活但类型不那么强的签名,它支持在多种源类型和目标类型之间进行转换。
此外,GenericConverter 还提供了源和目标类型描述符,你可以在实现转换逻辑时使用它们。
这些类型描述符允许类型转换由描述符源(例如字段或方法)上的注解或字段签名、方法签名等中声明的泛型信息驱动。
以下列表显示了 GenericConverter 接口的定义:
package org.springframework.core.convert.converter;
public interface GenericConverter {
public Set<ConvertiblePair> getConvertibleTypes();
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}
要实现 GenericConverter,让 getConvertibleTypes() 返回支持的
源 → 目标类型对。然后实现 convert(Object, TypeDescriptor,
TypeDescriptor) 以包含你的转换逻辑。源 TypeDescriptor 提供了
对持有正在转换的值的源字段或方法的访问。目标
TypeDescriptor 提供了对要设置转换值的目标字段或方法的访问。
GenericConverter 的一个很好的例子是数组和集合之间进行转换的转换器。
这样的 ArrayToCollectionConverter 内省声明目标集合类型的字段或方法,以解析集合的元素类型。
这允许在将集合设置到目标字段或提供给目标方法或构造函数之前,将源数组中的每个元素转换为集合元素类型。
|
由于 |
使用 ConditionalGenericConverter
有时,你希望 Converter 仅在特定条件成立时才运行。
例如,你可能希望 Converter 仅在目标字段或方法上存在特定注解时运行,
或者你可能希望 Converter 仅在目标类型上定义了特定方法(例如 static valueOf 方法)时运行。
ConditionalGenericConverter 是 GenericConverter 和
ConditionalConverter 接口的联合体,它允许你定义此类自定义匹配条件:
public interface ConditionalConverter {
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}
ConditionalGenericConverter 的一个很好的例子是 IdToEntityConverter,它在
持久化实体标识符和实体引用之间进行转换。这样的 IdToEntityConverter
可能仅在目标实体类型声明了静态查找方法(例如,findAccount(Long))时才匹配。
你可以在 matches(TypeDescriptor, TypeDescriptor) 的实现中执行此类查找方法检查。
ConversionService API
ConversionService 定义了一个统一的 API,用于在运行时执行类型转换逻辑。
转换器通常在以下门面接口后面运行:
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);
}
大多数 ConversionService 实现也实现了 ConverterRegistry,它提供了注册转换器的 SPI。
在内部,ConversionService 实现将类型转换逻辑委托给其注册的转换器来执行。
core.convert.support 包中提供了一个健壮的 ConversionService 实现。
GenericConversionService 是适用于大多数环境的通用实现。
ConversionServiceFactory 提供了一个方便的工厂,用于创建常见的 ConversionService 配置。
配置 ConversionService
ConversionService 是一个无状态对象,旨在在应用程序启动时实例化,然后在多个线程之间共享。
在 Spring 应用程序中,你通常为每个 Spring 容器(或 ApplicationContext)配置一个 ConversionService 实例。
Spring 会获取该 ConversionService 并在框架需要执行类型转换时使用它。
你也可以将此 ConversionService 注入到你的任何 bean 中并直接调用它。
|
如果没有 |
要向 Spring 注册一个默认的 ConversionService,请添加以下 bean 定义,其 id 为 conversionService:
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean"/>
默认的 ConversionService 可以在字符串、数字、枚举、集合、映射和其他常见类型之间进行转换。
要用你自己的自定义转换器补充或覆盖默认转换器,请设置 converters 属性。
属性值可以实现 Converter、ConverterFactory 或 GenericConverter 接口中的任何一个。
<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 章中的 转换和格式化。
在某些情况下,你可能希望在转换过程中应用格式化。
有关使用 FormattingConversionServiceFactoryBean 的详细信息,请参阅
The FormatterRegistry SPI。
以编程方式使用 ConversionService
要以编程方式使用 ConversionService 实例,你可以像注入任何其他 bean 一样注入它的引用。
以下示例展示了如何实现:
-
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,
你需要提供源类型和目标类型的正式定义。
幸运的是,TypeDescriptor 提供了各种选项,使其变得简单明了,
如以下示例所示:
- Java
-
DefaultConversionService cs = new DefaultConversionService(); List<Integer> input = ... cs.convert(input, TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Integer.class)), [id="CO1-1"][id="CO1-1"](1) TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class))); [id="CO1-2"][id="CO1-2"](2)
<1> `List<Integer>` 类型描述符 <1> `List<String>` 类型描述符
- Kotlin
-
val cs = DefaultConversionService() val input: List<Integer> = ... cs.convert(input, TypeDescriptor.collection(List::class.java, TypeDescriptor.valueOf(Integer::class.java)), [id="CO2-1"][id="CO2-1"](1) TypeDescriptor.collection(List::class.java, TypeDescriptor.valueOf(String::class.java))) [id="CO2-2"][id="CO2-2"](2)
<1> `List<Integer>` 类型描述符 <1> `List<String>` 类型描述符
请注意,DefaultConversionService 会自动注册适用于大多数环境的转换器。
这包括集合转换器、标量转换器和基本的 Object-to-String 转换器。
你可以通过使用 DefaultConversionService 类上的静态 addDefaultConverters
方法,将相同的转换器注册到任何 ConverterRegistry。
值类型的转换器会重复用于数组和集合,因此
没有必要创建特定的转换器来将 S 的 Collection 转换为 T 的
Collection,前提是标准集合处理是合适的。