spring可以对不同Class类型进行相应的转换。比如String转成Boolean. Boolean转成string类.以下是个简单的例子
@Test
public void testConverter() {
DefaultConversionService serivce = new DefaultConversionService();
boolean actual = serivce.canConvert(String.class, Boolean.class);
Assert.assertEquals(true, actual);
Object acc = serivce.convert("true", Boolean.class);
Assert.assertEquals(true, ((Boolean)acc).booleanValue());
}
spring类型转换涉及到的类图如下
1、spring是通过ConversionService进行类型转换,通过ConverterRegistry进行转换器注册
其中ConversionService中的方法canConverter判断是否可以转失,convert调用相应的转换器进行类型转换操作
ConverterRegistry中的方法add(Converter)是注册一个转换器
2、对于ConversionService和ConverterRegistry的主要实现类是GenericConversionService。GenericConversionService维护了一个内部类的对象Converters.
当注册一个converter时。会生成一个GenericeConverter的适配类ConverterAdapter,并注册到内部类Converters中
public void addConverter(Converter<?, ?> converter) {
GenericConverter.ConvertiblePair typeInfo = getRequiredTypeInfo(converter, Converter.class);
Assert.notNull(typeInfo, "Unable to the determine sourceType <S> and targetType " +
"<T> which your Converter<S, T> converts between; declare these generic types.");
addConverter(new ConverterAdapter(typeInfo, converter));
}
public void addConverter(Class<?> sourceType, Class<?> targetType, Converter<?, ?> converter) {
GenericConverter.ConvertiblePair typeInfo = new GenericConverter.ConvertiblePair(sourceType, targetType);
addConverter(new ConverterAdapter(typeInfo, converter));
}
public void addConverter(GenericConverter converter) {
this.converters.add(converter);
invalidateCache();
}
内部类Converters中维护了一个Map用于存放先前注册的所有Converter
private static class Converters {
private final Set<GenericConverter> globalConverters =
new LinkedHashSet<GenericConverter>();
private final Map<ConvertiblePair, ConvertersForPair> converters =
new LinkedHashMap<ConvertiblePair, ConvertersForPair>(36);
public void add(GenericConverter converter) {
Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes();
if (convertibleTypes == null) {
Assert.state(converter instanceof ConditionalConverter,
"Only conditional converters may return null convertible types");
globalConverters.add(converter);
}
else {
for (ConvertiblePair convertiblePair : convertibleTypes) {
ConvertersForPair convertersForPair = getMatchableConverters(convertiblePair);
convertersForPair.add(converter);
}
}
}
3、当调用canConvert时,会调用内部类的方法find方法查找转换的类型sourceType和targetType是否进行了注册
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
GenericConverter converter = this.converterCache.get(key);
if (converter != null) {
return (converter != NO_MATCH ? converter : null);
}
converter = this.converters.find(sourceType, targetType);
if (converter == null) {
converter = getDefaultConverter(sourceType, targetType);
}
if (converter != null) {
this.converterCache.put(key, converter);
return converter;
}
this.converterCache.put(key, NO_MATCH);
return null;
}
其中内部类Converters的find方法根据sourceType和targetType去查询Converters中维护的Map中是否包括支持的注册类型,如果存在返回GenericConverter的适配类ConverterAdapter,如果没有存在返回null
public GenericConverter find(TypeDescriptor sourceType, TypeDescriptor targetType) {
// Search the full type hierarchy
List<Class<?>> sourceCandidates = getClassHierarchy(sourceType.getType());
List<Class<?>> targetCandidates = getClassHierarchy(targetType.getType());
for (Class<?> sourceCandidate : sourceCandidates) {
for (Class<?> targetCandidate : targetCandidates) {
ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);
GenericConverter converter = getRegisteredConverter(
sourceType, targetType, convertiblePair);
if(converter != null) {
return converter;
}
}
}
return null;
}
private GenericConverter getRegisteredConverter(TypeDescriptor sourceType,
TypeDescriptor targetType, ConvertiblePair convertiblePair) {
// Check specifically registered converters
ConvertersForPair convertersForPair = converters.get(convertiblePair);
GenericConverter converter = convertersForPair == null ? null
: convertersForPair.getConverter(sourceType, targetType);
if (converter != null) {
return converter;
}
// Check ConditionalGenericConverter that match all types
for (GenericConverter globalConverter : this.globalConverters) {
if (((ConditionalConverter)globalConverter).matches(
sourceType, targetType)) {
return globalConverter;
}
}
return null;
}
4、当查找到注册的类型转换器时GenericConverter时。会调用ConverterAdapter中的convert方法。ConverterAdapter中的convert方法会调用相当维护的converter并转换
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return convertNullSource(sourceType, targetType);
}
return this.converter.convert(source);
}
5、spring的注册类型转换器时,是通过GenericConversionService的子类DefaultConversionService实现的
private static void addScalarConverters(ConverterRegistry converterRegistry) {
ConversionService conversionService = (ConversionService) converterRegistry;
converterRegistry.addConverter(new StringToBooleanConverter());
converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());
converterRegistry.addConverter(new StringToCharacterConverter());
converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new NumberToCharacterConverter());
converterRegistry.addConverterFactory(new CharacterToNumberFactory());
converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
converterRegistry.addConverter(Enum.class, String.class, new EnumToStringConverter(conversionService));
converterRegistry.addConverter(new StringToLocaleConverter());
converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new PropertiesToStringConverter());
converterRegistry.addConverter(new StringToPropertiesConverter());
converterRegistry.addConverter(new StringToUUIDConverter());
converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter());
}