[spring-core]类型转换机制
类型转换本质上来说是这么一个过程:
SourceType --> TargetType
Spring提供了一套基于Converter接口的SPI(Server Provide Interface)机制。
通过实现Converter接口,我们可以根据自己的业务需求制定特定的类型转换规则。
1 Converter接口
org.springframework.core.convert.converter.Converter:
public interface Converter {
@Nullable
T convert(S source);
default Converter andThen(Converter<? super T, ? extends U> after) {
Assert.notNull(after, "After Converter must not be null");
return (S s) -> {
T initialResult = convert(s);
return (initialResult != null ? after.convert(initialResult) : null);
};
}
}
泛型解释:
S:原始类型。T:目标类型。
方法解释:
convert():将数据由S类型转换到T类型。andThen():链式类型转换,S→T→U。
例如,我们想要实现一个Object → String的类型转换,可以按以下步骤进行:
- 实现
Converter接口,指定泛型S为Object,泛型T为String; - 实现
convert()方法,编写转换规则。
public class ObjectToStringConverter implements Converter
在Spring中则可以通过以下方式调用:
- 创建
ObjectToStringConverter,交由容器管理:
@Configuration
public class AppConfig {
@Bean
public Converter converter() {
return new ObjectToStringConverter();
}
}
- 在业务中注入
Converter实例:
@Service
public class AppService {
@Resource
private Converter converter;
public void service() {
Date source = new Date();
Object target = converter.convert(source);
System.out.println(target.getClass());
System.out.println(target);
}
}
- 外界调用
service()方法后,会输出以下内容,说明Date成功转换成String:
class java.lang.String
Thu Jan 13 21:33:49 CST 2022
Spring的类型转换机制本质上就是这么一个套路。通过实现Converter接口,可以针对性的实现不同的类型转换规则,适用于不同的业务场景。
当然,Spring也实现了许多常用的XxxConverter,需要进行类型转换时,可以先看看Converter的继承层次,是否可以直接使用。
2 ConverterFactory接口
为了便于管理一系列具有层次关系的Converter,Spring还提供了ConverterFactory接口。
org.springframework.core.convert.converter.ConverterFactory:
public interface ConverterFactory {
Converter getConverter(Class targetType);
}
泛型解释:
S:原始类型。R:目标类型的顶层父类/接口。T:特定目标类型,是R的子类/实现类。
方法解释:
getConverter():获取目标类型为T的Converter实现类。
例如,如果有以下业务需求:
Charater→IntegerCharater→FloatCharater→Long- ……
目标类型都是Number的子类,我们可以实现如下ConverterFactory:
public class CharacterToNumberFactory implements ConverterFactory {
@Override
public Converter getConverter(Class targetType) {
return new CharacterToNumber<>(targetType);
}
private static final class CharacterToNumber implements Converter {
private final Class targetType;
public CharacterToNumber(Class targetType) {
this.targetType = targetType;
}
@Override
public T convert(Character source) {
return NumberUtils.convertNumberToTargetClass((short) source.charValue(), this.targetType);
}
}
}
在Spring中则可以通过以下方式调用:
- 创建
CharacterToNumberFactory,交由容器管理:
@Configuration
public class AppConfig {
@Bean
public ConverterFactory converter() {
return new CharacterToNumberFactory();
}
}
- 在业务中注入
ConverterFactory实例:
@Service
public class AppService {
@Resource
private ConverterFactory converterFactory;
public void service() {
Character character = new Character('a');
Converter integerConverter = converterFactory.getConverter(Integer.class);
Object target = integerConverter.convert(character);
System.out.println(target.getClass());
System.out.println(target);
Converter doubleConverter = converterFactory.getConverter(Double.class);
target = doubleConverter.convert(character);
System.out.println(target.getClass());
System.out.println(target);
}
}
- 外界调用
service()方法后,会输出以下内容,说明Date成功转换成String:
class java.lang.Integer
97
class java.lang.Double
97.0
所以说,本质上ConverterFactory相当于是管理一系列具有特殊层次关系的Converter的工厂,相当于是Converter的升级版本。
在需要将同一个S类型转换成多个具有层次关系的T时,可以优先考虑实现ConverterFactory。
3 GenericConverter接口
org.springframework.core.convert.converter.GenericConverter接口以ConvertiblePair的形式指定sourceType和targetType:
public interface GenericConverter {
Set getConvertibleTypes();
Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
final class ConvertiblePair {
private final Class<?> sourceType;
private final Class<?> targetType;
}
}
GenericConverter不会在实现类中固定sourceType和targetType的数量和类型,相比于Converter接口有了更大的灵活度,但也更加复杂。
convert()方法中,需要根据sourceType和targetType的类型进行判断,执行对应的转换逻辑。
TypeDescriptor用来描述sourceType和targetType的类型信息:
public class TypeDescriptor implements Serializable {
private final Class<?> type;
private final ResolvableType resolvableType;
private final TypeDescriptor.AnnotatedElementAdapter annotatedElement;
}
举个例子,如果我们需要实现String → DataSize的类型转换,可以按以下例子进行:
final class StringToDataSizeConverter implements GenericConverter {
StringToDataSizeConverter() {
}
public Set getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(String.class, DataSize.class));
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return ObjectUtils.isEmpty(source) ? null : this.convert(source.toString(), this.getDataUnit(targetType));
}
private DataUnit getDataUnit(TypeDescriptor targetType) {
DataSizeUnit annotation = (DataSizeUnit)targetType.getAnnotation(DataSizeUnit.class);
return annotation != null ? annotation.value() : null;
}
private DataSize convert(String source, DataUnit unit) {
return DataSize.parse(source, unit);
}
}
4 ConditionalConverter接口
org.springframework.core.convert.converter.ConditionalConverter接口提供了matches()方法,用来判断当前Converter/GenericConverter/ConverterFactory能否完成从sourceType到targetType的转换。
public interface ConditionalConverter {
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
由于ConditionalConverter接口只提供了一个预校验的方法,它更像是一个辅助性功能接口。
它更多的跟其他实际业务接口配合使用,比如上述的Converter/ConverterFactory/GenericConverter等。
5 ConditionalGenericConverter接口
org.springframework.core.convert.converter.ConditionalGenericConverter接口将ConditionConverter和GenericConverter的功能整合了起来。
我们实现ConditionalGenericConverter接口就可以实现上述两个接口的功能。
public interface ConditionalGenericConverter
extends GenericConverter, ConditionalConverter {
}