对于最新的稳定版本,请使用 Spring Framework 7.0.6!spring-doc.cadn.net.cn

Spring类型转换

core.convert 包提供了一个通用类型转换系统。该系统定义了实现类型转换逻辑的 SPI 接口,以及用于在运行时执行类型转换的 API。在 Spring 容器中,您可以将此系统作为 PropertyEditor 实现的替代方案,将外部化的 Bean 属性值字符串转换为所需的属性类型。您也可以在应用程序中任何需要类型转换的地方使用此公共 API。spring-doc.cadn.net.cn

转换器SPI

实现类型转换逻辑的SPI很简单且类型强,如下面的接口定义所示:spring-doc.cadn.net.cn

package org.springframework.core.convert.converter;

public interface Converter<S, T> {

	T convert(S source);
}

要创建您自己的转换器,请实现 Converter 接口,并将 S 参数化为您要转换的类型,将 T 参数化为您要转换到的类型。如果需要将 S 的集合或数组透明地转换为 T 的数组或集合,也可以应用这样的转换器,前提是已注册了委托数组或集合转换器(DefaultConversionService 默认会注册)。spring-doc.cadn.net.cn

对于每次对 convert(S) 的调用,源参数保证不会为 null。如果转换失败,您的 Converter 可能会抛出任何未检查的异常。具体来说,它应该抛出一个 IllegalArgumentException 来报告无效的源值。 请注意确保您的 Converter 实现是线程安全的。spring-doc.cadn.net.cn

core.convert.support包中提供了几种转换器实现,作为便利。这些包括从字符串到数字和其他常见类型的转换器。 以下列表显示了StringToInteger类,这是一个典型的Converter实现:spring-doc.cadn.net.cn

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,如下例所示:spring-doc.cadn.net.cn

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 的子类。spring-doc.cadn.net.cn

StringToEnumConverterFactory 作为一个例子:spring-doc.cadn.net.cn

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 的接口定义:spring-doc.cadn.net.cn

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 提供对要设置转换后值的目标字段的访问。spring-doc.cadn.net.cn

一个GenericConverter的良好示例是将Java数组和集合之间进行转换的转换器。 这样的ArrayToCollectionConverter会检查声明目标集合类型的字段,以确定集合的元素类型。这使得源数组中的每个元素都可以在集合被设置到目标字段之前转换为集合的元素类型。spring-doc.cadn.net.cn

由于 GenericConverter 是一个更复杂的 SPI 接口,只有在需要时才应使用它。对于基本类型转换需求,应优先选择 ConverterConverterFactory

使用 ConditionalGenericConverter

有时,你希望一个 Converter 仅在特定条件为真时运行。例如,你可能希望仅当目标字段上有特定注解时才运行一个 Converter,或者你可能希望仅当目标类上定义了特定方法(如 static valueOf 方法)时才运行一个 ConverterConditionalGenericConverterGenericConverterConditionalConverter 接口的组合,它允许你定义这样的自定义匹配条件:spring-doc.cadn.net.cn

public interface ConditionalConverter {

	boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}

public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}

一个ConditionalGenericConverter的良好示例是将持久化实体标识符和实体引用之间进行转换的IdToEntityConverter。这种IdToEntityConverter可能仅在目标实体类型声明了一个静态查找方法时才匹配(例如,findAccount(Long))。您可以在matches(TypeDescriptor, TypeDescriptor)的实现中执行这样的查找方法检查。spring-doc.cadn.net.cn

ConversionService API

ConversionService 为在运行时执行类型转换逻辑定义了一个统一的 API。转换器通常在以下外观接口后面运行:spring-doc.cadn.net.cn

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 实现会将其注册的转换器来执行类型转换逻辑。spring-doc.cadn.net.cn

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

配置一个 ConversionService

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

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

要使用 Spring 注册一个默认的 ConversionService,请添加以下 bean 定义, 其 idconversionServicespring-doc.cadn.net.cn

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

默认的 ConversionService 可以在字符串、数字、枚举、集合、映射和其他常见类型之间进行转换。要使用自己的自定义转换器补充或覆盖默认转换器,请设置 converters 属性。属性值可以实现任何 ConverterConverterFactoryGenericConverter 接口。spring-doc.cadn.net.cn

<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章节中的 转换与格式化spring-doc.cadn.net.cn

在某些情况下,您可能希望在转换过程中应用格式。有关使用 FormattingConversionServiceFactoryBean 的详细信息,请参阅 The FormatterRegistry SPIspring-doc.cadn.net.cn

以编程方式使用ConversionService

要以编程方式使用 ConversionService 实例,您可以像其他任何 bean 一样注入对它的引用。下面的示例显示了如何操作:spring-doc.cadn.net.cn

@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(...)
	}
}

对于大多数用例,您可以使用指定targetTypeconvert方法,但它无法处理更复杂的类型,例如参数化元素的集合。 例如,如果您想将IntegerList程序化地转换为StringList,则需要提供源类型和目标类型的正式定义。spring-doc.cadn.net.cn

幸运的是,TypeDescriptor 提供了多种选项,使这变得简单明了,如下例所示:spring-doc.cadn.net.cn

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中。spring-doc.cadn.net.cn

用于值类型的转换器会用于数组和集合,因此不需要创建特定的转换器将 Collection 类型的 S 转换为 Collection 类型的 T,假设标准集合处理是合适的。spring-doc.cadn.net.cn