最近封装 RPC 相关的模块,领导说数据转换可以考虑使用 Spring 原有的 Converter 体系。
Converter<S, T>
最简单的转换器、相关的顶层接口有 ConditionalConverter
、GenericConverter
、ConverterFactory
、ConvertingComparator
、ConverterRegistry
ConversionService
Spring 数据转换的入口、它根据相关参数将调用路由到具体的 Converter。 相关的接口和类 FormattingConversionService
、DefaultConversionService
、ConversionServiceFactoryBean
、FormattingConversionServiceFactoryBean
Converter
/** * A converter converts a source object of type {@code S} to a target of type {@code T}. * * <p>Implementations of this interface are thread-safe and can be shared. * * <p>Implementations may additionally implement {@link ConditionalConverter}. * * @author Keith Donald * @since 3.0 * @param <S> the source type * @param <T> the target type */@FunctionalInterfacepublic interface Converter<S, T> { /** * Convert the source object of type {@code S} to target type {@code T}. * @param source the source object to convert, which must be an instance of {@code S} (never {@code null}) * @return the converted object, which must be an instance of {@code T} (potentially {@code null}) * @throws IllegalArgumentException if the source cannot be converted to the desired target type */ @Nullable T convert(S source);}
实现该接口要确保其线程安全、一般来说除了实现该接口、还会实现 ConditionalConverter
接口
看一些常见的 Spring 内部提供给我们使用的
字符串转布尔
final class StringToBooleanConverter implements Converter<String, Boolean> { private static final Set<String> trueValues = new HashSet<>(8); private static final Set<String> falseValues = new HashSet<>(8); static { trueValues.add("true"); trueValues.add("on"); trueValues.add("yes"); trueValues.add("1"); falseValues.add("false"); falseValues.add("off"); falseValues.add("no"); falseValues.add("0"); } @Override @Nullable public Boolean convert(String source) { String value = source.trim(); if (value.isEmpty()) { return null; } value = value.toLowerCase(); if (trueValues.contains(value)) { return Boolean.TRUE; } else if (falseValues.contains(value)) { return Boolean.FALSE; } else { throw new IllegalArgumentException("Invalid boolean value '" + source + "'"); } }}
字符转 Charset
class StringToCharsetConverter implements Converter<String, Charset> { @Override public Charset convert(String source) { return Charset.forName(source); }}
Converter 是一比一之间的转换、相对来说是比较简单的
ConverterFactory
这个是一比多之间的转换
public interface ConverterFactory<S, R> { /** * Get the converter to convert from S to target type T, where T is also an instance of R. * @param <T> the target type * @param targetType the target type to convert to * @return a converter from S to T */ <T extends R> Converter<S, T> getConverter(Class<T> targetType);}
看下 String 转为各种枚举类
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> { @Override public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) { return new StringToEnum(ConversionUtils.getEnumType(targetType)); } private static class StringToEnum<T extends Enum> implements Converter<String, T> { private final Class<T> enumType; public StringToEnum(Class<T> enumType) { this.enumType = enumType; } @Override @Nullable public T convert(String source) { if (source.isEmpty()) { // It's an empty enum identifier: reset the enum value to null. return null; } return (T) Enum.valueOf(this.enumType, source.trim()); } }}
GenericConverter
N:N 的转换
public interface GenericConverter { // 这里就返回了转换关系、是一个集合 @Nullable Set<ConvertiblePair> getConvertibleTypes(); @Nullable Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType); final class ConvertiblePair { private final Class<?> sourceType; private final Class<?> targetType; public ConvertiblePair(Class<?> sourceType, Class<?> targetType) { this.sourceType = sourceType; this.targetType = targetType; } }}
看一下 ObjectToOptionalConverter
final class ObjectToOptionalConverter implements ConditionalGenericConverter { private final ConversionService conversionService; public ObjectToOptionalConverter(ConversionService conversionService) { this.conversionService = conversionService; } @Override public Set<ConvertiblePair> getConvertibleTypes() { Set<ConvertiblePair> convertibleTypes = new LinkedHashSet<>(4); convertibleTypes.add(new ConvertiblePair(Collection.class, Optional.class)); convertibleTypes.add(new ConvertiblePair(Object[].class, Optional.class)); convertibleTypes.add(new ConvertiblePair(Object.class, Optional.class)); return convertibleTypes; } @Override public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { if (targetType.getResolvableType().hasGenerics()) { return this.conversionService.canConvert(sourceType, new GenericTypeDescriptor(targetType)); } else { return true; } } @Override public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { if (source == null) { return Optional.empty(); } else if (source instanceof Optional) { return source; } else if (targetType.getResolvableType().hasGenerics()) { Object target = this.conversionService.convert(source, sourceType, new GenericTypeDescriptor(targetType)); if (target == null || (target.getClass().isArray() && Array.getLength(target) == 0) || (target instanceof Collection && ((Collection<?>) target).isEmpty())) { return Optional.empty(); } return Optional.of(target); } else { return Optional.of(source); } } @SuppressWarnings("serial") private static class GenericTypeDescriptor extends TypeDescriptor { public GenericTypeDescriptor(TypeDescriptor typeDescriptor) { super(typeDescriptor.getResolvableType().getGeneric(), null, typeDescriptor.getAnnotations()); } }}
这里借助了 ConversionService
协助将 source 对象转换为 Optional<T>
中 T 泛型对象
可以看到 getConvertibleTypes
返回的是三个转换关系的。
ConversionService
作为整个转换系统的入口
public interface ConversionService { boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType); boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType); @Nullable <T> T convert(@Nullable Object source, Class<T> targetType); @Nullable Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);}
如果是泛型相关建议使用 TypeDescriptor
参数的 converter
方法
我们看一下 ConverterRegistry
ConfigurableConversionService
ConfigurableConversionService
只是做了两个接口的整合、啥都没做
public interface ConfigurableConversionService extends ConversionService, ConverterRegistry {}
GenericConversionService
GenericConversionService
是 ConversionService
和 ConverterRegistry
的实现类
public class GenericConversionService implements ConfigurableConversionService { // 当不需要进行转换的时候使用该转换器、也就是源对象的类型是目标类型或者其子类 private static final GenericConverter NO_OP_CONVERTER = new NoOpConverter("NO_OP"); // 没有找到合适的转换器、使用该转换器作为占位符、放在缓存 Map 中占位 private static final GenericConverter NO_MATCH = new NoOpConverter("NO_MATCH"); // 内部类、用来管理 Converter 的 private final Converters converters = new Converters(); // 缓存、可以看到 value 是 GenericConverter private final Map<ConverterCacheKey, GenericConverter> converterCache = new ConcurrentReferenceHashMap<>(64); @Override public void addConverter(Converter<?, ?> converter) { ResolvableType[] typeInfo = getRequiredTypeInfo(converter.getClass(), Converter.class); // 转为 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) { // 转为 GenericConverter addConverter(new ConverterAdapter( converter, ResolvableType.forClass(sourceType), ResolvableType.forClass(targetType))); } @Override public void addConverter(GenericConverter converter) { this.converters.add(converter); invalidateCache(); } @Override public void addConverterFactory(ConverterFactory<?, ?> factory) { // 获取声明的泛型信息 ResolvableType[] typeInfo = getRequiredTypeInfo(factory.getClass(), ConverterFactory.class); ........ // 转为 GenericConverter addConverter(new ConverterFactoryAdapter(factory, new ConvertiblePair(typeInfo[0].toClass(), typeInfo[1].toClass()))); } @Override public void removeConvertible(Class<?> sourceType, Class<?> targetType) { this.converters.remove(sourceType, targetType); invalidateCache(); } @Override public boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType) { Assert.notNull(targetType, "Target type to convert to cannot be null"); return canConvert((sourceType != null ? TypeDescriptor.valueOf(sourceType) : null), TypeDescriptor.valueOf(targetType)); } @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); } public boolean canBypassConvert(@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 == NO_OP_CONVERTER); } @Override @SuppressWarnings("unchecked") @Nullable public <T> T convert(@Nullable Object source, Class<T> targetType) { Assert.notNull(targetType, "Target type to convert to cannot be null"); return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType)); } @Override @Nullable public Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) { ....... 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); }
DefaultConversionService
DefaultConversionService
提供方法为 ConverterRegistry 增加一些常用的 Converter
final class StringToBooleanConverter implements Converter<String, Boolean> { private static final Set<String> trueValues = new HashSet<>(8); private static final Set<String> falseValues = new HashSet<>(8); static { trueValues.add("true"); trueValues.add("on"); trueValues.add("yes"); trueValues.add("1"); falseValues.add("false"); falseValues.add("off"); falseValues.add("no"); falseValues.add("0"); } @Override @Nullable public Boolean convert(String source) { String value = source.trim(); if (value.isEmpty()) { return null; } value = value.toLowerCase(); if (trueValues.contains(value)) { return Boolean.TRUE; } else if (falseValues.contains(value)) { return Boolean.FALSE; } else { throw new IllegalArgumentException("Invalid boolean value '" + source + "'"); } }}0
FormattingConversionService
FormattingConversionService 则实现了 FormatterRetistry 接口
final class StringToBooleanConverter implements Converter<String, Boolean> { private static final Set<String> trueValues = new HashSet<>(8); private static final Set<String> falseValues = new HashSet<>(8); static { trueValues.add("true"); trueValues.add("on"); trueValues.add("yes"); trueValues.add("1"); falseValues.add("false"); falseValues.add("off"); falseValues.add("no"); falseValues.add("0"); } @Override @Nullable public Boolean convert(String source) { String value = source.trim(); if (value.isEmpty()) { return null; } value = value.toLowerCase(); if (trueValues.contains(value)) { return Boolean.TRUE; } else if (falseValues.contains(value)) { return Boolean.FALSE; } else { throw new IllegalArgumentException("Invalid boolean value '" + source + "'"); } }}1
Printer 主要是为了打印展示某个类型对象的、根据 locale
final class StringToBooleanConverter implements Converter<String, Boolean> { private static final Set<String> trueValues = new HashSet<>(8); private static final Set<String> falseValues = new HashSet<>(8); static { trueValues.add("true"); trueValues.add("on"); trueValues.add("yes"); trueValues.add("1"); falseValues.add("false"); falseValues.add("off"); falseValues.add("no"); falseValues.add("0"); } @Override @Nullable public Boolean convert(String source) { String value = source.trim(); if (value.isEmpty()) { return null; } value = value.toLowerCase(); if (trueValues.contains(value)) { return Boolean.TRUE; } else if (falseValues.contains(value)) { return Boolean.FALSE; } else { throw new IllegalArgumentException("Invalid boolean value '" + source + "'"); } }}2
Parser 则是从 String 根据 locale 解释为 T、这个跟 Converter 优点类似、但是是根据 Locale 的不用来解释转换的
final class StringToBooleanConverter implements Converter<String, Boolean> { private static final Set<String> trueValues = new HashSet<>(8); private static final Set<String> falseValues = new HashSet<>(8); static { trueValues.add("true"); trueValues.add("on"); trueValues.add("yes"); trueValues.add("1"); falseValues.add("false"); falseValues.add("off"); falseValues.add("no"); falseValues.add("0"); } @Override @Nullable public Boolean convert(String source) { String value = source.trim(); if (value.isEmpty()) { return null; } value = value.toLowerCase(); if (trueValues.contains(value)) { return Boolean.TRUE; } else if (falseValues.contains(value)) { return Boolean.FALSE; } else { throw new IllegalArgumentException("Invalid boolean value '" + source + "'"); } }}3
Formatter 则直接整合这两个接口
无论是 Printer 或者是 Parser 最终都会转换为 GenericConverter、在 convert 方法中在调用对应的方法进行转换解释。
ApplicationConversionService
Spring Boot 默认使用的 ConversionService 。
但是在 Spring 中管理的却是 WebConversionService。它最终也会调用 addBeans 方法
final class StringToBooleanConverter implements Converter<String, Boolean> { private static final Set<String> trueValues = new HashSet<>(8); private static final Set<String> falseValues = new HashSet<>(8); static { trueValues.add("true"); trueValues.add("on"); trueValues.add("yes"); trueValues.add("1"); falseValues.add("false"); falseValues.add("off"); falseValues.add("no"); falseValues.add("0"); } @Override @Nullable public Boolean convert(String source) { String value = source.trim(); if (value.isEmpty()) { return null; } value = value.toLowerCase(); if (trueValues.contains(value)) { return Boolean.TRUE; } else if (falseValues.contains(value)) { return Boolean.FALSE; } else { throw new IllegalArgumentException("Invalid boolean value '" + source + "'"); } }}4
并且会将所有的 GenericConverter、Converter、Printer、Parser bean 注册到 FormatterRegistry 中
Converter VS PropertyEditor
PropertyEditor 从 String 转为其他类型
Converter 从各种类型转为各种类型
Spring 同时支持两者。
BeanWrapper 实现了 TypeConverter、而 TypeConverter 则 先使用PropertyEditor转换器器转换,如果没找到对应的转换器器,会⽤ConversionService来进⾏行行对象转换。将两者整合起来做转换。
ConditionalConverter
final class StringToBooleanConverter implements Converter<String, Boolean> { private static final Set<String> trueValues = new HashSet<>(8); private static final Set<String> falseValues = new HashSet<>(8); static { trueValues.add("true"); trueValues.add("on"); trueValues.add("yes"); trueValues.add("1"); falseValues.add("false"); falseValues.add("off"); falseValues.add("no"); falseValues.add("0"); } @Override @Nullable public Boolean convert(String source) { String value = source.trim(); if (value.isEmpty()) { return null; } value = value.toLowerCase(); if (trueValues.contains(value)) { return Boolean.TRUE; } else if (falseValues.contains(value)) { return Boolean.FALSE; } else { throw new IllegalArgumentException("Invalid boolean value '" + source + "'"); } }}5
当你实现了 Converter 接口和 ConditionalConverter 接口的时候、在 ConverterAdapter 适配类中会回调你的 matchs 方法
还有一种情况是
当存在多个一样的 sourceType 和 targetType 的转换器时、怎么选择出一个合适的 Converter
final class StringToBooleanConverter implements Converter<String, Boolean> { private static final Set<String> trueValues = new HashSet<>(8); private static final Set<String> falseValues = new HashSet<>(8); static { trueValues.add("true"); trueValues.add("on"); trueValues.add("yes"); trueValues.add("1"); falseValues.add("false"); falseValues.add("off"); falseValues.add("no"); falseValues.add("0"); } @Override @Nullable public Boolean convert(String source) { String value = source.trim(); if (value.isEmpty()) { return null; } value = value.toLowerCase(); if (trueValues.contains(value)) { return Boolean.TRUE; } else if (falseValues.contains(value)) { return Boolean.FALSE; } else { throw new IllegalArgumentException("Invalid boolean value '" + source + "'"); } }}6
实际问题
封装 RPC 模块过程中、涉及到各种类型的转换、有 json和 PoJo 的转换、也有 xml 和 PoJo 的转换、更有一些自定义协议报文到 PoJo 的转换、而这些 PoJo 具体是什么类型、在 RPC 的底层模块中是不知道的、只有在运行时通过泛型相关的信息获取的
比如 Json 和 PoJo 之间的转换、可能需要两个转换器、一个是 String 到 PoJo 的、一个是 PoJo 到 String
PoJo 到 String 是比较简单的、这里讨论下 String 到 PoJo。
String 到 PoJo 、这个关系貌似就是 1:N 的关系、貌似使用 ConverterFactory 就可以解决
第一个是泛型问题、而且作为 ConverterFactory 需要手动注册到 ConverterRegistry 中、
最终选定 GenericConverter、
final class StringToBooleanConverter implements Converter<String, Boolean> { private static final Set<String> trueValues = new HashSet<>(8); private static final Set<String> falseValues = new HashSet<>(8); static { trueValues.add("true"); trueValues.add("on"); trueValues.add("yes"); trueValues.add("1"); falseValues.add("false"); falseValues.add("off"); falseValues.add("no"); falseValues.add("0"); } @Override @Nullable public Boolean convert(String source) { String value = source.trim(); if (value.isEmpty()) { return null; } value = value.toLowerCase(); if (trueValues.contains(value)) { return Boolean.TRUE; } else if (falseValues.contains(value)) { return Boolean.FALSE; } else { throw new IllegalArgumentException("Invalid boolean value '" + source + "'"); } }}7
getConvertibleTypes 返回 null 的话、必须实现接口 ConditionalConverter
此 Converter 作为 globalConverter
final class StringToBooleanConverter implements Converter<String, Boolean> { private static final Set<String> trueValues = new HashSet<>(8); private static final Set<String> falseValues = new HashSet<>(8); static { trueValues.add("true"); trueValues.add("on"); trueValues.add("yes"); trueValues.add("1"); falseValues.add("false"); falseValues.add("off"); falseValues.add("no"); falseValues.add("0"); } @Override @Nullable public Boolean convert(String source) { String value = source.trim(); if (value.isEmpty()) { return null; } value = value.toLowerCase(); if (trueValues.contains(value)) { return Boolean.TRUE; } else if (falseValues.contains(value)) { return Boolean.FALSE; } else { throw new IllegalArgumentException("Invalid boolean value '" + source + "'"); } }}8原文:https://juejin.cn/post/7098322657061371941