Spring Type Conversion
core.convert
包提供了一个通用类型转换系统。该系统定义了一个用于实现类型转换逻辑的 SPI 和一个用于在运行时执行类型转换的 API。在 Spring 容器中,你可以将此系统用作 PropertyEditor
实现的替代方案,以将外部化 Bean 属性值字符串转换为所需的属性类型。你还可以在你需要类型转换的应用程序中的任何地方使用公共 API。
Converter SPI
用于实现类型转换逻辑的 SPI 非常简单且类型化,如下面的接口定义所示:
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
实现:
final class StringToInteger implements Converter<String, Integer> {
public Integer convert(String source) {
return Integer.valueOf(source);
}
}
Using ConverterFactory
当需要集中转换整个类层次结构的逻辑时(例如,从 String
转换为 Enum
对象时),你可以实现 ConverterFactory
,如下面的示例所示:
public interface ConverterFactory<S, R> {
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
将 S 参数化为你正在从中转换的类型,将 R 参数化为定义你可以转换为的类的 范围 的基类型。然后实现 getConverter(Class<T>)
,其中 T 是 R 的子类。
以 StringToEnumConverterFactory
为例:
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
的接口定义:
public interface GenericConverter {
public Set<ConvertiblePair> getConvertibleTypes();
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}
要实现 GenericConverter
,让 getConvertibleTypes()
返回支持的源→目标类型对。然后实现 convert(Object, TypeDescriptor, TypeDescriptor)
来包含你的转换逻辑。源 TypeDescriptor
提供对包含要转换的值的源字段的访问。目标 TypeDescriptor
提供对将转换后的值设置到的目标字段的访问。
GenericConverter
的一个好例子是可以在 Java 数组和集合之间进行转换的转换器。此类 ArrayToCollectionConverter
内省声明目标集合类型的字段,以解析集合的元素类型。这允许在将集合设置到目标字段之前将源数组中的每个元素转换为集合元素类型。
因为 |
Using ConditionalGenericConverter
有时,你希望只有在特定条件成立时才运行 Converter
。例如,你可能希望只有在目标字段上存在特定注释时才运行 Converter
,或者你可能希望只有在目标类上定义特定方法(例如 static valueOf
方法)时才运行 Converter
。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)
的实现中执行此类查找器方法检查。
The ConversionService
API
ConversionService
定义了一个统一的 API,用于在运行时执行类型转换逻辑。转换器通常在以下 facades 接口后面运行:
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
配置的工厂。
Configuring a ConversionService
ConversionService
是一个无状态对象,设计为在应用程序启动时实例化,然后在多个线程之间共享。在 Spring 应用程序中,通常为每个 Spring 容器(或 ApplicationContext
)配置一个 ConversionService
实例。Spring 会获取该 ConversionService
,并在框架需要执行类型转换时使用它。您还可以在任何 bean 中注入此 ConversionService
并直接调用它。
如果没有向 Spring 注册 |
要使用 Spring 注册默认的 ConversionService
,请添加以下具有 id
为 conversionService
的 bean 定义:
<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 章节中的Conversion and Formatting。
在某些情况下,你可能希望在转换期间应用格式。有关使用 FormattingConversionServiceFactoryBean
的详细信息,请参阅 The FormatterRegistry
SPI。
Using a ConversionService
Programmatically
要以编程方式使用 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
-
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
中。
值类型的转换器可用于数组和集合,因此无需创建特定的转换器来从 S
的 Collection
转换为 T
的 Collection
,假设标准集合处理是适当的。