续上章,我们还是从具体实现指出看看,Spring Converter到底是怎么工作的。
目录
注册Converter 1 2 3 4 private static void addScalarConverters (ConverterRegistry converterRegistry) { converterRegistry.addConverterFactory(new NumberToNumberConverterFactory ()); converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter ()); }
这里我们发现其实有2个不同的注册方法,分别是 addConverter 和 addConverterFactory,我们先看看addConverter。
1 2 3 4 public <S, T> void addConverter (Class<S> sourceType, Class<T> targetType, Converter<? super S, ? extends T> converter) { addConverter(new ConverterAdapter ( converter, ResolvableType.forClass(sourceType), ResolvableType.forClass(targetType))); }
其实这里是把最终的ObjectToStringConverter的包装成一个ConverterAdapter,而去看ConverterAdapter的声明我们会发现,他最终的接口表现形式是ConditionalGenericConverter。 那问题也清晰起来,还记得上一章,我们聊到ConditionalGenericConverter是一个带条件的转换,具体能否调用,我们需要看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public boolean matches (TypeDescriptor sourceType, TypeDescriptor targetType) { if (this .typeInfo.getTargetType() != targetType.getObjectType()) { return false ; } ResolvableType rt = targetType.getResolvableType(); if (!(rt.getType() instanceof Class) && !rt.isAssignableFrom(this .targetType) && !this .targetType.hasUnresolvableGenerics()) { return false ; } return !(this .converter instanceof ConditionalConverter) || ((ConditionalConverter) this .converter).matches(sourceType, targetType); }
这里我们发现最终注册的ConverterAdapter,是需要比较S和T再确定是否能够调用的。
这个matches 其实很有意思。最后一步比较其实就允许 ConditionalConverter的2层嵌套。
继续看另外一个addConverterFactory
1 2 3 4 5 6 7 8 9 10 11 12 public void addConverterFactory (ConverterFactory<?, ?> factory) { ResolvableType[] typeInfo = getRequiredTypeInfo(factory.getClass(), ConverterFactory.class); if (typeInfo == null && factory instanceof DecoratingProxy) { typeInfo = getRequiredTypeInfo(((DecoratingProxy) factory).getDecoratedClass(), ConverterFactory.class); } if (typeInfo == null ) { throw new IllegalArgumentException ("Unable to determine source type <S> and target type <T> for your " + "ConverterFactory [" + factory.getClass().getName() + "]; does the class parameterize those types?" ); } addConverter(new ConverterFactoryAdapter (factory, new ConvertiblePair (typeInfo[0 ].resolve(), typeInfo[1 ].resolve()))); }
这里我们也发现最终也是得到了ConverterFactoryAdapter,这个就不去细看,和上面那个ConverterAdapter极为相似,我们从中学会了设计模式-适配器模式 . 果然Java处处是设计模式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private static class Converters { private final Set<GenericConverter> globalConverters = new LinkedHashSet <GenericConverter>(); 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" ); this .globalConverters.add(converter); } else { for (ConvertiblePair convertiblePair : convertibleTypes) { ConvertersForPair convertersForPair = getMatchableConverters(convertiblePair); convertersForPair.add(converter); } } } }
最终所有的Covert都通过Adapter适配到,我们的核心容器 globalConverters 中。
调用Converter 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public Object convert (Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { Assert.notNull(targetType, "targetType to convert to cannot be null" ); if (sourceType == null ) { Assert.isTrue(source == null , "source must be [null] if sourceType == [null]" ); return handleResult(null , targetType, convertNullSource(null , targetType)); } if (source != null && !sourceType.getObjectType().isInstance(source)) { throw new IllegalArgumentException ("source to convert from must be an instance of " + sourceType + "; instead it was a " + source.getClass().getName()); } GenericConverter converter = getConverter(sourceType, targetType); if (converter != null ) { Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType); return handleResult(sourceType, targetType, result); } return handleConverterNotFound(source, sourceType, targetType); }
我们可以发现 getConverter(sourceType, targetType) 这一行,就是查询Converter的核心。而调用的过程就很简单,直接调用Convert.convert()方法就OK了,那我们把注意力放在查询的过程。
查询Converter 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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 ; }
这行代码,我们就发现一个在Spring里面用的特别多的设计理念,包括我们平时也可以用的就是缓存。我们下次在自己的项目里可以考虑多使用缓存。关于这个,我也会在后续写一点关于Cache使用的小技巧出来(好像给自己又挖了一个坑)。 那我们继续看find这个方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public GenericConverter find (TypeDescriptor sourceType, TypeDescriptor targetType) { 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 ; }
Wow,我第一次看到这里极为的惊叹,这就是Spring的能力而我所达不到的,所有的声明起的也很清晰,这里是拿出所有的继承的类型。 这样话,可能我们注册的Convert是一个Source类型的父类,那我们也是可以从中获得匹配的方法,在我们没有和入参一样类型的情况下。继续进去看getRegisteredConverter这个方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private GenericConverter getRegisteredConverter (TypeDescriptor sourceType, TypeDescriptor targetType, ConvertiblePair convertiblePair) { ConvertersForPair convertersForPair = this .converters.get(convertiblePair); if (convertersForPair != null ) { GenericConverter converter = convertersForPair.getConverter(sourceType, targetType); if (converter != null ) { return converter; } } for (GenericConverter globalConverter : this .globalConverters) { if (((ConditionalConverter) globalConverter).matches(sourceType, targetType)) { return globalConverter; } } return null ; }
从这段代码里面,我们就能看出,Spring给用户拓展留下的入口是converters这个变量,自己维护的是globalConverters。而ConvertersForPair有兴趣看的读者可以也是维护了一个GenericConverter的数组。 可见最终所有的converter想要可用都是需要调用matches进行类型匹配的,所以我们自己现实一个 Convert 也是不可完全不顾 Source 的类型从而实现一个。
到这里,Convert服务的注册,发现,调用。 我们都已经了解,但是还缺点什么。比如我们经常会有这样的需求,比如把 List A 转换成 List B,如果我们还需要自己实现岂不是特别笨。 当然Spring这么聪明也为我们想到了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public static void addCollectionConverters (ConverterRegistry converterRegistry) { ConversionService conversionService = (ConversionService) converterRegistry; converterRegistry.addConverter(new ArrayToCollectionConverter (conversionService)); converterRegistry.addConverter(new CollectionToArrayConverter (conversionService)); converterRegistry.addConverter(new ArrayToArrayConverter (conversionService)); converterRegistry.addConverter(new CollectionToCollectionConverter (conversionService)); converterRegistry.addConverter(new MapToMapConverter (conversionService)); converterRegistry.addConverter(new ArrayToStringConverter (conversionService)); converterRegistry.addConverter(new StringToArrayConverter (conversionService)); converterRegistry.addConverter(new ArrayToObjectConverter (conversionService)); converterRegistry.addConverter(new ObjectToArrayConverter (conversionService)); converterRegistry.addConverter(new CollectionToStringConverter (conversionService)); converterRegistry.addConverter(new StringToCollectionConverter (conversionService)); converterRegistry.addConverter(new CollectionToObjectConverter (conversionService)); converterRegistry.addConverter(new ObjectToCollectionConverter (conversionService)); if (streamAvailable) { converterRegistry.addConverter(new StreamConverter (conversionService)); } }
在注册服务的,我们从名字也可以看出,提供了很多类似于 Array to Collection 方法,而这些Convert是没有具体的类型转换的,是负责将容器互相转换。 我们就举个ArrayToCollectionConverter的例子。
我们看看
1 2 3 4 5 6 7 org.springframework.core.convert.support.ArrayToCollectionConverter public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { return ConversionUtils.canConvertElements( sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor(), this.conversionService); }
ArrayToCollectionConverter的匹配方式是是单独的工具类。源码中的注释写的很清楚就不解释了。我们还看看如何转换的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public Object convert (Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { if (source == null ) { return null ; } int length = Array.getLength(source); TypeDescriptor elementDesc = targetType.getElementTypeDescriptor(); Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), (elementDesc != null ? elementDesc.getType() : null ), length); if (elementDesc == null ) { for (int i = 0 ; i < length; i++) { Object sourceElement = Array.get(source, i); target.add(sourceElement); } } else { for (int i = 0 ; i < length; i++) { Object sourceElement = Array.get(source, i); Object targetElement = this .conversionService.convert(sourceElement, sourceType.elementTypeDescriptor(sourceElement), elementDesc); target.add(targetElement); } } return target; }
其实从某种角度上看,也应是是这样的,分为构造返回的Collections类型,然后遍历调用。这里就发现cache的重要性了,因为增加的缓存,我们在大规模的List转换的时候,查找只需要进行一次就可以了。
总结 Spring-Convert 虽然只是Spring 的一个小功能,但是其实这个使用的地方特别多,比如HttpRequest里面获得原始类型都是String,也就是通过ConvertService 转换成不同的Int,Long等等。 也算是阅读Spring源码的一点点收获。