Spring ConversionService 類型轉換(一)Converter

Spring ConversionService 類型轉換(一)Converter

Spring 系列目錄(http://www.javashuo.com/article/p-kqecupyl-bm.html)html

Spring ConversionService 類型轉換系列文章:java

  1. Spring ConversionService 類型轉換(一)Converter
  2. Spring ConversionService 類型轉換(二)ConversionService

JDK 提供的 PropertyEditor 只能將 String 類型轉爲 Object,若是要將一種 Object 類型轉換成另外一種 Object 類型就只能使用 Spring 提供的 ConversionService 了,這些類都位於 org.springframework.core.convert 包下。spring

1、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 默認的覺轉換器,能夠分爲如下幾類:ide

  • Converter 一對一轉換,把 S 類型轉化成 T 類型,最經常使用的轉換器
  • ConverterFactory 一對 N 轉換
  • GenericConverter N 對 N 轉換

2、三種轉換器

三種轉換器

2.1 Converter(1:1)

(1) 接口學習

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

Converter接口很簡單,就是把 S 類型轉化成 T 類型。咱們看一下使用方法:測試

(2) 測試ui

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

(3) ObjectToStringConverter 分析this

ObjectToStringConverter 和 StringToBooleanConverter 都是在 DefaultConversionService 中內置的。code

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

2.2 ConverterFactory(1:N)

(1) 接口htm

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》:http://www.javashuo.com/article/p-zhacqikj-gk.html

天天用心記錄一點點。內容也許不重要,但習慣很重要!

相關文章
相關標籤/搜索