GenericConverter
用于在两个或多个类型之间转换的通用转换器接口。这是最灵活的转换器SPI接口,也是最复杂的
灵活是因为它一个转换器就能转换多个s/t,所以它是N->N的。实现类们一般情况下也会实现接口:ConditionalConverter
1个GenericConverter支持转化的所有类型都写在了属性Set内
public interface GenericConverter { @Nullable Set<ConvertiblePair> getConvertibleTypes(); @Nullable Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType); /** * Holder for a source-to-target class pair. */ // 包含有一对 s和t final class ConvertiblePair { private final Class<?> sourceType; private final Class<?> targetType; public ConvertiblePair(Class<?> sourceType, Class<?> targetType) { Assert.notNull(sourceType, "Source type must not be null"); Assert.notNull(targetType, "Target type must not be null"); this.sourceType = sourceType; this.targetType = targetType; } ... // 去掉get/set方法 以及toString equals等基础方法 } }
它的实现类都是子接口ConditionalGenericConverter
的实现类(就是GenericConverter和ConditionalConverter的结合).
注意:Spring的所有内部实现,依旧全部未公开,因此本文只举例说明一下即可。
final class ArrayToObjectConverter implements ConditionalGenericConverter { // 借助了ConversionService private final ConversionService conversionService; public ArrayToObjectConverter(ConversionService conversionService) { this.conversionService = conversionService; } // 残暴:都是object @Override public Set<ConvertiblePair> getConvertibleTypes() { return Collections.singleton(new ConvertiblePair(Object[].class, Object.class)); } // 实现ConditionalConverter的方法,最终是委托给了ConversionService#canConvert方法 @Override public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { return ConversionUtils.canConvertElements(sourceType.getElementTypeDescriptor(), targetType, this.conversionService); } ... } // 这里的转换器,都和数组、集合有关,比如: // StringToCollectionConverter、CollectionToArrayConverter、CollectionToStringConverter // StringToArrayConverter、StreamConverter、CollectionToArrayConverter等等
特别说一句:这里有一个非常有意思的转换器:IdToEntityConverter,SpringMVC默认给我们这已经注册进去了,在Spring MVC自定义常用的、通用的Controller的时候,我们会借助它实现通用方案,让controller异常的方便,好使~~~暂时可先参考:路由id转化为控制器Entity参数
ConverterRegistry
使用ConverterRegistry可以使我们对类型转换器做一个统一的注册。正如前言所说的,要实现自己的类型转换逻辑我们可以实现Converter接口、ConverterFactory接口和GenericConverter接口,ConverterRegistry接口就分别为这三种类型提供了对应的注册方法,至于里面的逻辑就可以发挥自己的设计能力进行设计实现了。
通过ConverterAdapter或者ConverterFactoryAdapter最后都会转化成GenericConverter,我想应该是因为这种converter是最通用的原因吧
一般而言:我们在实现ConversionService接口的时候也会实现ConverterRegistry接口
// @since 3.0 Converter 注册处,用于存储 Converter 实例 public interface ConverterRegistry { void addConverter(Converter<?, ?> converter); // 添加一个 Converter 实例,并指定其源和目标类型 <S, T> void addConverter(Class<S> sourceType, Class<T> targetType, Converter<? super S, ? extends T> converter); void addConverter(GenericConverter converter); void addConverterFactory(ConverterFactory<?, ?> factory); // 移除方法只有一个:它是面向s和t来做移除的~~~~ 删除所有匹配指定源和目标类型的 Converter // Remove any converters from {@code sourceType} to {@code targetType} void removeConvertible(Class<?> sourceType, Class<?> targetType); }
两大分支。FormatterRegistry用于注册格式化器,下面再说
ConfigurableConversionService:它就是把ConversionService和ConverterRegistry绑定在一起,自己并不提供新接口
// @since 3.1 public interface ConfigurableConversionService extends ConversionService, ConverterRegistry { }
所以它的具体内容,放到ConversionService里描述吧。
ConversionService
用于类型转换的服务接口。这是转换系统的**入口点**。请保证它convert方法的线程安全,这个接口非常的重要。
举个例子,使用Environment的<T> T getProperty(String key, Class<T> targetType)这里的类型转换,就是要通过ConversionService来完成的。
// @since 3.0 public interface ConversionService { // 特别说明:若是Map、集合、数组转换时。即使下面方法convert转换抛出了异常,这里也得返回true 因为Spring希望调用者处理这个异常:ConversionException boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType); boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType); // 注意此处:转换的source都是对象,target只需要类型即可~~~ @Nullable <T> T convert(@Nullable Object source, Class<T> targetType); @Nullable Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType); }
GenericConversionService
它也并不是一个抽象类,它是一个通用的处理。但是一般不会直接使用它,而是使用它的更具体的子类
// @since 3.0 实现了接口ConversionService和ConverterRegistry public class GenericConversionService implements ConfigurableConversionService { // 啥都不做,但是呢conversion is not required,相当于占位的意思 private static final GenericConverter NO_OP_CONVERTER = new NoOpConverter("NO_OP"); // 当转换器缓存中没有任何匹配时,它上场 // 请不要把它直接return,用null代替返回 private static final GenericConverter NO_MATCH = new NoOpConverter("NO_MATCH"); // 说明:Converter是一个静态内部类 它会Manages all converters registered with the service private final Converters converters = new Converters(); // 缓存转换器。用的ConcurrentReferenceHashMap是Spring自己实现的一个软引用/弱引用的Map private final Map<ConverterCacheKey, GenericConverter> converterCache = new ConcurrentReferenceHashMap<>(64); // 仅有一个空构造函数,构造函数内啥都没做 @Override public void addConverter(Converter<?, ?> converter) { // 这个处理很有意思:getRequiredTypeInfo 拿到两个泛型参数类型(若没有指定泛型 返回的是null) ResolvableType[] typeInfo = getRequiredTypeInfo(converter.getClass(), Converter.class); // Decorate和Proxy模式的区别。Decorate模式可用于函数防抖 Proxy模式就是我们常用的代理模式 if (typeInfo == null && converter instanceof DecoratingProxy) { typeInfo = getRequiredTypeInfo(((DecoratingProxy) converter).getDecoratedClass(), Converter.class); } // 由此可见这个转换器的泛型类型是必须的~~~ if (typeInfo == null) { throw new IllegalArgumentException("Unable to determine source type <S> and target type <T> for your " + "Converter [" + converter.getClass().getName() + "]; does the class parameterize those types?"); } // ConverterAdapter是个GenericConverter。由此课件最终都是转换成了GenericConverter类型 addConverter(new ConverterAdapter(converter, typeInfo[0], typeInfo[1])); } @Override 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))); } // 最终都是转换成了GenericConverter 进行转换器的保存 全部放在Converters里保存着 @Override public void addConverter(GenericConverter converter) { this.converters.add(converter); invalidateCache(); // 清空缓存 } // 使用ConverterFactoryAdapter转换成GenericConverter @Override public void addConverterFactory(ConverterFactory<?, ?> factory) { ... } // 注意ConvertiblePair是重写了equals方法和hash方法的 @Override public void removeConvertible(Class<?> sourceType, Class<?> targetType) { this.converters.remove(sourceType, targetType); invalidateCache(); } // 主要是getConverter() 方法 相当于只有有转换器匹配,就是能够被转换的 @Override public boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType) { Assert.notNull(targetType, "Target type to convert to cannot be null"); if (sourceType == null) { return true; } GenericConverter converter = getConverter(sourceType, targetType); return (converter != null); } @Nullable protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) { ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType); GenericConverter converter = this.converterCache.get(key); // 这个处理:如果缓存有值 但是为NO_MATCH 那就返回null,而不是把No_Match直接return if (converter != null) { return (converter != NO_MATCH ? converter : null); } converter = this.converters.find(sourceType, targetType); if (converter == null) { converter = getDefaultConverter(sourceType, targetType); } // 如果默认的不为null 也可以return的 // NO_OP_CONVERTER还是可以return的~~~ if (converter != null) { this.converterCache.put(key, converter); return converter; } this.converterCache.put(key, NO_MATCH); return null; } @Nullable protected GenericConverter getDefaultConverter(TypeDescriptor sourceType, TypeDescriptor targetType) { return (sourceType.isAssignableTo(targetType) ? NO_OP_CONVERTER : null); } // 拿到泛型类型们 @Nullable private ResolvableType[] getRequiredTypeInfo(Class<?> converterClass, Class<?> genericIfc) { ResolvableType resolvableType = ResolvableType.forClass(converterClass).as(genericIfc); ResolvableType[] generics = resolvableType.getGenerics(); if (generics.length < 2) { return null; } Class<?> sourceType = generics[0].resolve(); Class<?> targetType = generics[1].resolve(); if (sourceType == null || targetType == null) { return null; } return generics; } ... }
绝大多数情况下,我们不会直接使用GenericConversionService
,而是使用它的子类DefaultConversionService
DefaultConversionService
它能适用于绝大多数的场景中。
// @since 3.1 public class DefaultConversionService extends GenericConversionService { // @since 4.3.5 改变量出现得还是比较晚的 @Nullable private static volatile DefaultConversionService sharedInstance; // 空构造,那就注册到自己this身上~~~因为自己也是个ConverterRegistry public DefaultConversionService() { addDefaultConverters(this); } // 就是把sharedInstance返回出去~~~(永远不可能返回null) public static ConversionService getSharedInstance() { ... } // 默认情况下,这个ConversionService注册的转换器们~~~~ 几乎涵盖了所有~~~~ public static void addDefaultConverters(ConverterRegistry converterRegistry) { addScalarConverters(converterRegistry); addCollectionConverters(converterRegistry); converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry)); converterRegistry.addConverter(new StringToTimeZoneConverter()); converterRegistry.addConverter(new ZoneIdToTimeZoneConverter()); converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter()); converterRegistry.addConverter(new ObjectToObjectConverter()); converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry)); converterRegistry.addConverter(new FallbackObjectToStringConverter()); converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry)); } ... }
从源码可以看出,它几乎覆盖注册了所有的通用的类型转换,若涉及到自定义的对象的转换,亦可自己自定义转换器。
备注:DefaultConversionService它在PropertyResolver、org.springframework.jdbc.core.RowMapper、org.springframework.expression.TypeConverter…也就是properties、el表达式里、spring-jdbc数据封装的类型转换里都有应用
关于FormattingConversionService,它和格式化有关,所以放在Formatter章节里了,可参考:
【小家Spring】聊聊Spring中的格式化:Formatter、AnnotationFormatterFactory、DateFormatter以及@DateTimeFormat…
ConversionServiceFactoryBean
它是我们自定义转换器
的一个入口。比如之前我们见过这么配置的自定义转换器:
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <ref bean="userConverter"/> </set> </property> </bean>
这样,我们的自定义的转换器userConverter
就被添加进去了。我们在Spring MVC中需要自定义转换器的时候,也是这么来弄的。(使用java配置的方式添加,此处省略)
它的源码比较简单:
public class ConversionServiceFactoryBean implements FactoryBean<ConversionService>, InitializingBean { // 保存着我们diy set捡来的转换器们 @Nullable private Set<?> converters; // 最终是一个DefaultConversionService,然后向里添加自定义的转换器~ @Nullable private GenericConversionService conversionService; // Bean初始化结束后,注册自定义的转换器进去~~ @Override public void afterPropertiesSet() { this.conversionService = createConversionService(); ConversionServiceFactory.registerConverters(this.converters, this.conversionService); } protected GenericConversionService createConversionService() { return new DefaultConversionService(); } @Override @Nullable public ConversionService getObject() { return this.conversionService; } // 最终是个GenericConversionService,实际是个DefaultConversionService @Override public Class<? extends ConversionService> getObjectType() { return GenericConversionService.class; } @Override public boolean isSingleton() { return true; } }
另外,如果你还需要格式化的功能,使用FormattingConversionServiceFactoryBean代替即可
Spring中的PropertyEditor属性编辑器
在文末稍微介绍一下Spring中的PropertyEditor属性编辑器,因为它和类型转换器特别的像。
PropertyEditor是JavaBean规范定义的接口,这是java.beans中一个接口,其设计的意图是图形化编程上,方便对象与String之间的转换工作,而spring将其扩展,方便各种对象与String之间的转换工作。
Spring所有的扩展都是通过继承PropertyEditorSupport,因为它只聚焦于转换上,所以只需复写setAsText()、getAsText()以及构造方法即可实现扩展。
Spring 使用PropertyEditors的接口来实现对象和字符串之间的转换,比如将 2007-14-09转化为日期类型等,可以通过注册自定义编辑器来实现此功能
下面贴出Spring内置的一些属性编辑器们:
这些PropertyEditors都位于org.springframework.beans.propertyeditors
包中,大多是都是由BeanWrapperImpl注册,当属性编辑器以某种方式进行配置时,开发者仍可以注册自定义的变体用于覆盖默认的变量