Spring类型转换
core.convert
包提供了一个通用的类型转换系统。该系统定义了一个SPI来实现类型转换逻辑,并提供了一个API在运行时执行类型转换。在Spring容器中,您可以使用此系统作为将外部化的bean属性值字符串转换为所需属性类型的替代方案,而不是使用PropertyEditor
实现。您还可以在应用程序中的任何地方使用公共API,需要进行类型转换时。
转换器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);
}
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
接口。与
Converter
相比,
GenericConverter
具有更灵活但不那么强类型的签名,支持在多个源和目标类型之间进行转换。此外,
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
示例是在Java数组和集合之间进行转换的转换器。这样的
ArrayToCollectionConverter
会检查声明目标集合类型的字段,以解析集合的元素类型。这样,在将源数组中的每个元素转换为集合元素类型之前,将集合设置到目标字段上。
由于GenericConverter 是一个更复杂的SPI接口,只有在需要时才应使用它。对于基本的类型转换需求,请优先考虑使用Converter 或ConverterFactory 。 |
使用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)
的实现中执行这样的查找方法检查。
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 ,则会使用原始基于PropertyEditor 的系统。 |
要向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章节中的转换和格式化。
在某些情况下,您可能希望在转换过程中应用格式化。有关使用FormattingConversionServiceFactoryBean
的详细信息,请参阅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
-
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
,假设标准的集合处理是适当的。