Spring ConversionService 类型转换(一)Converter

Spring ConversionService 类型转换(一)Converter

Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html)

Spring ConversionService 类型转换系列文章:

  1. Spring ConversionService 类型转换(一)Converter
  2. Spring ConversionService 类型转换(二)ConversionService

JDK 提供的 PropertyEditor 只能将 String 类型转为 Object,如果要将一种 Object 类型转换成另一种 Object 类型就只能使用 Spring 提供的 ConversionService 了,这些类都位于 org.springframework.core.convert 包下。

一、ConversionService 使用

private ConversionService conversionService;
@Before
public void before() {
    conversionService = new DefaultConversionService();
}

@Test
public void test() {
    ConversionService conversionService = new DefaultConversionService();
    Integer value = conversionService.convert("1", Integer.class);
    Assert.assertTrue(value == 1);
}

在 DefaultConversionService 组件中已经注册了 Spring 默认的觉转换器,可以分为以下几类:

  • Converter 一对一转换,把 S 类型转化成 T 类型,最常用的转换器
  • ConverterFactory 一对 N 转换
  • GenericConverter N 对 N 转换

二、三种转换器

三种转换器

2.1 Converter(1:1)

(1) 接口

@FunctionalInterface
public interface Converter<S, T> {
    T convert(S source);
}

Converter接口很简单,就是把 S 类型转化成 T 类型。我们看一下使用方法:

(2) 测试

@Test
public void converterTest() {
    // ObjectToStringConverter
    Assert.assertEquals("false", conversionService.convert(false, String.class));
    
    // StringToBooleanConverter
    Assert.assertTrue(conversionService.convert("true", Boolean.class));
}

(3) ObjectToStringConverter 分析

ObjectToStringConverter 和 StringToBooleanConverter 都是在 DefaultConversionService 中内置的。

final class ObjectToStringConverter implements Converter<Object, String> {
    @Override
    public String convert(Object source) {
        return source.toString();
    }
}

2.2 ConverterFactory(1:N)

(1) 接口

public interface ConverterFactory<S, R> {
    <T extends R> Converter<S, T> getConverter(Class<T> targetType);
}

R 的子类都可以统一由这个 ConverterFactory 进行转换。

(2) 测试

// 测试 ConverterFactory StringToNumberConverterFactory
@Test
public void converterFactoryTest() {
    Assert.assertTrue(conversionService.convert("1.2", double.class) == 1.2d);
    Assert.assertTrue(conversionService.convert("1", int.class) == 1);
    Assert.assertTrue(conversionService.convert("0x10", byte.class) == 0x10);
}

这里用到了 StringToNumberConverterFactory 把 String 转化成了 Number 的各个子类型,代码其实很简单:

(3) StringToNumberConverterFactory 分析

final class StringToNumberConverterFactory implements ConverterFactory<String, Number> {
    @Override
    public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {
        return new StringToNumber<>(targetType);
    }

    private static final class StringToNumber<T extends Number> implements Converter<String, T> {
        private final Class<T> targetType;
        public StringToNumber(Class<T> targetType) {
            this.targetType = targetType;
        }

        @Override
        public T convert(String source) {
            if (source.isEmpty()) {
                return null;
            }
            // String 类型转换成 Number
            return NumberUtils.parseNumber(source, this.targetType);
        }
    }
}

2.3 GenericConverter(N:N)

(1) 接口

public interface GenericConverter {
    // 可以转换的类型
    Set<ConvertiblePair> getConvertibleTypes();

    Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);

    final class ConvertiblePair {
        private final Class<?> sourceType;
        private final Class<?> targetType;
    }
}

// 匹配 GenericConverter
public interface ConditionalConverter {
    boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}

public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}

GenericConverter 是 N:N 的转化,支持转化的所有类型都写在了属性 Set 内。

(2) 测试

// 测试 GenericConverter CollectionToCollectionConverter
@Test
public void genericConverterTest() {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
    Set<String> set = (Set<String>) conversionService.convert(list,
            TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Integer.class)),
            TypeDescriptor.collection(Set.class, TypeDescriptor.valueOf(String.class)));
    // List<Integer> -> Set<String>
    Assert.assertEquals("1", set.toArray(new String[0])[0]);
}

这里用到了 CollectionToCollectionConverter

(3) CollectionToCollectionConverter 分析

final class CollectionToCollectionConverter implements ConditionalGenericConverter {
    private final ConversionService conversionService;
    public CollectionToCollectionConverter(ConversionService conversionService) {
        this.conversionService = conversionService;
    }

    @Override
    public Set<ConvertiblePair> getConvertibleTypes() {
        return Collections.singleton(new ConvertiblePair(Collection.class, Collection.class));
    }

    @Override
    public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
        return ConversionUtils.canConvertElements(
                sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor(), this.conversionService);
    }

    @Override
    public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        if (source == null) {
            return null;
        }
        Collection<?> sourceCollection = (Collection<?>) source;

        // 集合类型
        boolean copyRequired = !targetType.getType().isInstance(source);
        // 1. targetType 集合类型没变,不用转换
        if (!copyRequired && sourceCollection.isEmpty()) {
            return source;
        }

        // 集合元素类型
        TypeDescriptor elementDesc = targetType.getElementTypeDescriptor();
        // 2. targetType 集合元素没有指定类型,即 Object,且集合类型没变
        if (elementDesc == null && !copyRequired) {
            return source;
        }

        // 创建一个空集合
        Collection<Object> target = CollectionFactory.createCollection(targetType.getType(),
                (elementDesc != null ? elementDesc.getType() : null), sourceCollection.size());

        // 3. targetType 集合元素没有指定类型,则元素不用转换类型
        if (elementDesc == null) {
            target.addAll(sourceCollection);
        // 4. conversionService 将 sourceElement 转换为 targetElement 类型
        } else {
            for (Object sourceElement : sourceCollection) {
                Object targetElement = this.conversionService.convert(sourceElement,
                        sourceType.elementTypeDescriptor(sourceElement), elementDesc);
                target.add(targetElement);
                if (sourceElement != targetElement) {
                    copyRequired = true;
                }
            }
        }
        return (copyRequired ? target : source);
    }
}

参考:

  1. 《Spring 学习记录 3 ConversionService》:https://www.cnblogs.com/abcwt112/p/7447435.html

每天用心记录一点点。内容也许不重要,但习惯很重要!

原文地址:https://www.cnblogs.com/binarylei/p/10263581.html