首页>>后端>>Spring->Spring Converter 体系

Spring Converter 体系

时间:2023-11-30 本站 点击:0

最近封装 RPC 相关的模块,领导说数据转换可以考虑使用 Spring 原有的 Converter 体系。

Converter<S, T> 最简单的转换器、相关的顶层接口有 ConditionalConverterGenericConverterConverterFactoryConvertingComparatorConverterRegistry

ConversionService Spring 数据转换的入口、它根据相关参数将调用路由到具体的 Converter。 相关的接口和类 FormattingConversionServiceDefaultConversionServiceConversionServiceFactoryBeanFormattingConversionServiceFactoryBean

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

GenericConversionServiceConversionServiceConverterRegistry 的实现类

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


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/Spring/4519.html