概述
最近封装 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
*/
@FunctionalInterface
public 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
public class DefaultConversionService extends GenericConversionService {
@Nullable
private static volatile DefaultConversionService sharedInstance;
public DefaultConversionService() {
addDefaultConverters(this);
}
public static ConversionService getSharedInstance() {
DefaultConversionService cs = sharedInstance;
if (cs == null) {
synchronized (DefaultConversionService.class) {
cs = sharedInstance;
if (cs == null) {
cs = new DefaultConversionService();
sharedInstance = cs;
}
}
}
return cs;
}
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));
}
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));
converterRegistry.addConverter(new StreamConverter(conversionService));
}
private static void addScalarConverters(ConverterRegistry converterRegistry) {
converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());
converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToCharacterConverter());
converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new NumberToCharacterConverter());
converterRegistry.addConverterFactory(new CharacterToNumberFactory());
converterRegistry.addConverter(new StringToBooleanConverter());
converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
converterRegistry.addConverter(new EnumToStringConverter((ConversionService) converterRegistry));
converterRegistry.addConverterFactory(new IntegerToEnumConverterFactory());
converterRegistry.addConverter(new EnumToIntegerConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new StringToLocaleConverter());
converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToCharsetConverter());
converterRegistry.addConverter(Charset.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToCurrencyConverter());
converterRegistry.addConverter(Currency.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToPropertiesConverter());
converterRegistry.addConverter(new PropertiesToStringConverter());
converterRegistry.addConverter(new StringToUUIDConverter());
converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter());
}
}
FormattingConversionService
FormattingConversionService 则实现了 FormatterRetistry 接口
/**
* Prints objects of type T for display.
*
* @author Keith Donald
* @since 3.0
* @param <T> the type of object this Printer prints
*/
@FunctionalInterface
public interface Printer<T> {
/**
* Print the object of type T for display.
* @param object the instance to print
* @param locale the current user locale
* @return the printed text string
*/
String print(T object, Locale locale);
}
Printer 主要是为了打印展示某个类型对象的、根据 locale
/**
* Parses text strings to produce instances of T.
*
* @author Keith Donald
* @since 3.0
* @param <T> the type of object this Parser produces
*/
@FunctionalInterface
public interface Parser<T> {
/**
* Parse a text String to produce a T.
* @param text the text string
* @param locale the current user locale
* @return an instance of T
* @throws ParseException when a parse exception occurs in a java.text parsing library
* @throws IllegalArgumentException when a parse exception occurs
*/
T parse(String text, Locale locale) throws ParseException;
}
Parser 则是从 String 根据 locale 解释为 T、这个跟 Converter 优点类似、但是是根据 Locale 的不用来解释转换的
/**
* Formats objects of type T.
* A Formatter is both a Printer <i>and</i> a Parser for an object type.
*
* @author Keith Donald
* @since 3.0
* @param <T> the type of object this Formatter formats
*/
public interface Formatter<T> extends Printer<T>, Parser<T> {
}
Formatter 则直接整合这两个接口
无论是 Printer 或者是 Parser 最终都会转换为 GenericConverter、在 convert 方法中在调用对应的方法进行转换解释。
ApplicationConversionService
Spring Boot 默认使用的 ConversionService 。
但是在 Spring 中管理的却是 WebConversionService。它最终也会调用 addBeans 方法
public class ApplicationConversionService extends FormattingConversionService {
private static volatile ApplicationConversionService sharedInstance;
public ApplicationConversionService() {
this(null);
}
public ApplicationConversionService(StringValueResolver embeddedValueResolver) {
if (embeddedValueResolver != null) {
setEmbeddedValueResolver(embeddedValueResolver);
}
configure(this);
}
public static ConversionService getSharedInstance() {
ApplicationConversionService sharedInstance = ApplicationConversionService.sharedInstance;
if (sharedInstance == null) {
synchronized (ApplicationConversionService.class) {
sharedInstance = ApplicationConversionService.sharedInstance;
if (sharedInstance == null) {
sharedInstance = new ApplicationConversionService();
ApplicationConversionService.sharedInstance = sharedInstance;
}
}
}
return sharedInstance;
}
public static void configure(FormatterRegistry registry) {
DefaultConversionService.addDefaultConverters(registry);
DefaultFormattingConversionService.addDefaultFormatters(registry);
addApplicationFormatters(registry);
addApplicationConverters(registry);
}
public static void addApplicationConverters(ConverterRegistry registry) {
addDelimitedStringConverters(registry);
registry.addConverter(new StringToDurationConverter());
registry.addConverter(new DurationToStringConverter());
registry.addConverter(new NumberToDurationConverter());
registry.addConverter(new DurationToNumberConverter());
registry.addConverter(new StringToPeriodConverter());
registry.addConverter(new PeriodToStringConverter());
registry.addConverter(new NumberToPeriodConverter());
registry.addConverter(new StringToDataSizeConverter());
registry.addConverter(new NumberToDataSizeConverter());
registry.addConverter(new StringToFileConverter());
registry.addConverter(new InputStreamSourceToByteArrayConverter());
registry.addConverterFactory(new LenientStringToEnumConverterFactory());
registry.addConverterFactory(new LenientBooleanToEnumConverterFactory());
}
public static void addDelimitedStringConverters(ConverterRegistry registry) {
ConversionService service = (ConversionService) registry;
registry.addConverter(new ArrayToDelimitedStringConverter(service));
registry.addConverter(new CollectionToDelimitedStringConverter(service));
registry.addConverter(new DelimitedStringToArrayConverter(service));
registry.addConverter(new DelimitedStringToCollectionConverter(service));
}
public static void addApplicationFormatters(FormatterRegistry registry) {
registry.addFormatter(new CharArrayFormatter());
registry.addFormatter(new InetAddressFormatter());
registry.addFormatter(new IsoOffsetFormatter());
}
public static void addBeans(FormatterRegistry registry, ListableBeanFactory beanFactory) {
Set<Object> beans = new LinkedHashSet<>();
beans.addAll(beanFactory.getBeansOfType(GenericConverter.class).values());
beans.addAll(beanFactory.getBeansOfType(Converter.class).values());
beans.addAll(beanFactory.getBeansOfType(Printer.class).values());
beans.addAll(beanFactory.getBeansOfType(Parser.class).values());
for (Object bean : beans) {
if (bean instanceof GenericConverter) {
registry.addConverter((GenericConverter) bean);
}
else if (bean instanceof Converter) {
registry.addConverter((Converter<?, ?>) bean);
}
else if (bean instanceof Formatter) {
registry.addFormatter((Formatter<?>) bean);
}
else if (bean instanceof Printer) {
registry.addPrinter((Printer<?>) bean);
}
else if (bean instanceof Parser) {
registry.addParser((Parser<?>) bean);
}
}
}
}
并且会将所有的 GenericConverter、Converter、Printer、Parser bean 注册到 FormatterRegistry 中
Converter VS PropertyEditor
- PropertyEditor 从 String 转为其他类型
- Converter 从各种类型转为各种类型
Spring 同时支持两者。
BeanWrapper 实现了 TypeConverter、而 TypeConverter 则 先使用PropertyEditor转换器器转换,如果没找到对应的转换器器,会⽤ConversionService来进⾏行行对象转换。将两者整合起来做转换。
ConditionalConverter
public interface ConditionalConverter {
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}
当你实现了 Converter 接口和 ConditionalConverter 接口的时候、在 ConverterAdapter 适配类中会回调你的 matchs 方法
还有一种情况是
当存在多个一样的 sourceType 和 targetType 的转换器时、怎么选择出一个合适的 Converter
private static class ConvertersForPair {
private final Deque<GenericConverter> converters = new ConcurrentLinkedDeque<>();
public void add(GenericConverter converter) {
this.converters.addFirst(converter);
}
@Nullable
public GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
for (GenericConverter converter : this.converters) {
if (!(converter instanceof ConditionalGenericConverter) ||
((ConditionalGenericConverter) converter).matches(sourceType, targetType)) {
return converter;
}
}
return null;
}
@Override
public String toString() {
return StringUtils.collectionToCommaDelimitedString(this.converters);
}
}
实际问题
封装 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、
@Component
public class JsonStringToObjectConverter implements
GenericConverter , ConditionalConverter {
@Autowired
private ObjectMapper objectMapper;
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return null;
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
try {
if (source instanceof JsonStringMessage) {
return objectMapper.readValue(((JsonStringMessage) source).getSource(), targetType.getType());
}else {
// impossible
throw new IllegalArgumentException("illegal argument");
}
} catch (JsonProcessingException e) {
throw new IllegalStateException(e);
}
}
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return sourceType.getType().equals(JsonStringMessage.class);
}
getConvertibleTypes 返回 null 的话、必须实现接口 ConditionalConverter
此 Converter 作为 globalConverter
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) {
getMatchableConverters(convertiblePair).add(converter);
}
}
}
最后
以上就是单身硬币为你收集整理的Spring Converter 体系的全部内容,希望文章能够帮你解决Spring Converter 体系所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复