Spring Converter 体系(一)

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

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

  • Converter 最简单的转换器、相关的顶层接口有 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
 */
@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 泛型对象

可以看到 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);
  }
复制代码


目录
相关文章
|
6月前
|
JSON 前端开发 Java
解决Spring MVC中No converter found for return value of type异常
在Spring MVC开发中遇到`No converter found for return value of type`异常,通常是因缺少消息转换器、返回值类型不支持或转换器优先级配置错误。解决方案包括:1) 添加对应的消息转换器,如`MappingJackson2HttpMessageConverter`;2) 自定义消息转换器并实现`HttpMessageConverter`接口,设置优先级;3) 修改返回值类型为如`ResponseEntity`的合适类型。通过这些方法可确保返回值正确转换为响应内容。
492 1
|
4月前
|
XML JSON Java
spring,springBoot配置类型转化器Converter以及FastJsonHttpMessageConverter,StringHttpMessageConverter 使用
spring,springBoot配置类型转化器Converter以及FastJsonHttpMessageConverter,StringHttpMessageConverter 使用
579 1
|
机器学习/深度学习 XML 前端开发
【小家Spring】聊聊Spring中的数据转换:Converter、ConversionService、TypeConverter、PropertyEditor(下)
【小家Spring】聊聊Spring中的数据转换:Converter、ConversionService、TypeConverter、PropertyEditor(下)
【小家Spring】聊聊Spring中的数据转换:Converter、ConversionService、TypeConverter、PropertyEditor(下)
|
前端开发 Java Spring
Spring MVC-06循序渐进之Converter和Formatter
Spring MVC-06循序渐进之Converter和Formatter
81 0
|
前端开发 安全 Java
【小家Spring】聊聊Spring中的数据转换:Converter、ConversionService、TypeConverter、PropertyEditor(中)
【小家Spring】聊聊Spring中的数据转换:Converter、ConversionService、TypeConverter、PropertyEditor(中)
【小家Spring】聊聊Spring中的数据转换:Converter、ConversionService、TypeConverter、PropertyEditor(中)
|
机器学习/深度学习 前端开发 Java
【小家Spring】聊聊Spring中的数据转换:Converter、ConversionService、TypeConverter、PropertyEditor(上)
【小家Spring】聊聊Spring中的数据转换:Converter、ConversionService、TypeConverter、PropertyEditor(上)
【小家Spring】聊聊Spring中的数据转换:Converter、ConversionService、TypeConverter、PropertyEditor(上)
|
XML JSON Java
Spring Converter 体系(二)
最近封装 RPC 相关的模块,领导说数据转换可以考虑使用 Spring 原有的 Converter 体系。
260 0
|
2月前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。
|
24天前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
125 2
|
3月前
|
缓存 Java Maven
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决