跳到主要内容
版本:7.0.3

Spring类型转换

Hunyuan 7b 中英对照 Spring Type Conversion

core.convert 包提供了一个通用的类型转换系统。该系统定义了一个 SPI(Service Provider Interface)来实现类型转换逻辑,以及一个 API 用于在运行时执行类型转换。在 Spring 容器中,你可以使用这个系统作为 PropertyEditor 实现的替代方案,将外部化的bean属性值字符串转换为所需的属性类型。你还可以在应用程序中任何需要类型转换的地方使用这个公共 API。

转换器 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>)方法,其中TR的子类。

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接口,因此只有在确实需要时才应使用它。对于基本的类型转换需求,建议优先使用ConverterConverterFactory

使用ConditionalGenericConverter

有时,你可能希望只有在特定条件成立时才运行Converter。例如,你可能希望在目标字段或方法上存在特定注释时才运行Converter,或者你可能希望在目标类型上定义了特定方法(如static valueOf方法)时才运行ConverterConditionalGenericConverter结合了GenericConverterConditionalConverter接口的功能,允许你定义这样的自定义匹配条件:

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,用于在运行时执行类型转换逻辑。转换器通常通过以下外观接口(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);
}

大多数ConversionService实现也实现了ConverterRegistry,后者提供了一个用于注册转换器的SPI(Service Provider Interface)。在内部,ConversionService的实现会将其类型转换逻辑委托给已注册的转换器来执行。

core.convert.support包中提供了一个强大的ConversionService实现。GenericConversionService是适用于大多数环境的通用实现。ConversionServiceFactory提供了一个便捷的工厂,用于创建常见的ConversionService配置。

配置 ConversionService

ConversionService是一个无状态对象,设计用于在应用程序启动时实例化,然后在多个线程之间共享。在Spring应用程序中,通常为每个Spring容器(或ApplicationContext)配置一个ConversionService实例。每当框架需要进行类型转换时,Spring就会使用这个ConversionService。你也可以将这个ConversionService注入到任何Bean中,并直接调用它。

备注

如果Spring中没有注册ConversionService,则会使用基于原始PropertyEditor的系统。

要在Spring中注册一个默认的ConversionService,请添加以下带有idconversionService的bean定义:

<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean"/>

默认的ConversionService可以在线程、数字、枚举类型(enum)、集合(collections)、映射(maps)以及其他常见类型之间进行转换。为了补充或覆盖这些默认转换器,你可以使用自己的自定义转换器,只需设置converters属性即可。该属性的值可以实现ConverterConverterFactoryGenericConverter接口中的任意一种。

<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一样注入对其的引用。以下示例展示了如何操作:

@Service
public class MyService {

private final ConversionService conversionService;

public MyService(ConversionService conversionService) {
this.conversionService = conversionService;
}

public void doIt() {
this.conversionService.convert(...)
}
}

对于大多数使用场景,你可以使用指定targetTypeconvert方法,但它无法处理更复杂的类型,例如参数化元素的集合。例如,如果你想将一个Integer类型的List编程转换为String类型的List,你需要提供源类型和目标类型的正式定义。

幸运的是,TypeDescriptor提供了多种选项来简化这一过程,如下例所示:

DefaultConversionService cs = new DefaultConversionService();

List<Integer> input = ...
cs.convert(input,
>TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Integer.class)), 1
>TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class))); 2
  • List<Integer> 类型描述符

  • List<String> 类型描述符

请注意,DefaultConversionService 会自动注册适用于大多数环境的转换器。这些转换器包括集合转换器、标量转换器以及基本的 ObjectString 的转换器。您可以通过使用 DefaultConversionService 类上的静态方法 addDefaultConverters,将相同的转换器注册到任何 ConverterRegistry 中。

对于值类型的转换器,它们也可以被重用来处理数组和集合。因此,假设标准的集合处理方法是合适的,就没有必要专门创建一个转换器来将类型为 S 的集合转换为类型为 T 的集合。