上一章簡單介紹了Converter
接口的使用,Converter
接口是用於明確原類型和目標類型之間的轉換。java
那麼怎麼才能從原類型轉換到某一類的目標類型呢?好比字符串轉枚舉類型,我有PersonType和PersonStatus兩個枚舉類型,那麼就得有兩個轉換器PersonTypeConverter
、PersonStatusConverter
,枚舉類型再多點,Converter
也跟着多。因此Spring提供了一個工廠接口org.springframework.core.convert.converter.ConverterFactory
。spring
public interface ConverterFactory<S, R> { <T extends R> Converter<S, T> getConverter(Class<T> targetType); }
其中泛型S是source原類型,R是目標類型, T是R的子類型。數組
和通常的工廠模式同樣,這就是提供給咱們一個根據子類型返回相應轉換器的工具。mvc
若是按Converter
的思路作,代碼應該是這樣子:dom
public class StringToPersonStatusConverter implements Converter<String, PersonStatus> { @Override public PersonStatus convert(String source) { if (Objects.isNull(source)) { return null; } return PersonStatus.valueOf(source); } }
public class StringToPersonTypeConverter implements Converter<String, PersonType> { @Override public PersonType convert(String source) { if (Objects.isNull(source)) { return null; } return PersonType.valueOf(source); } }
每一個枚舉都要寫轉換器也太麻煩了,咱們可使用ConverterFactory
抽象出轉換過程。ide
package org.springframework.core.convert.support; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.ConverterFactory; @SuppressWarnings({"unchecked", "rawtypes"}) final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> { @Override public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) { Class<?> enumType = targetType; while (enumType != null && !enumType.isEnum()) { enumType = enumType.getSuperclass(); } if (enumType == null) { throw new IllegalArgumentException( "The target type " + targetType.getName() + " does not refer to an enum"); } return new StringToEnum(enumType); } private class StringToEnum<T extends Enum> implements Converter<String, T> { private final Class<T> enumType; public StringToEnum(Class<T> enumType) { this.enumType = enumType; } @Override public T convert(String source) { if (source.length() == 0) { // It's an empty enum identifier: reset the enum value to null. return null; } return (T) Enum.valueOf(this.enumType, source.trim()); } } }
這就是Spring內置的StringToEnumConverterFactory
源碼。工具
下面這個org.springframework.core.convert.converter.GenericConverter
接口是全部的Converter
接口中最靈活也是最複雜的一個類型轉換接口。以前介紹的Converter
接口只支持從一個原類型轉換爲一個目標類型;ConverterFactory
接口只支持從一個原類型轉換爲一個目標類型對應的子類型;而GenericConverter
接口支持在多個不一樣的原類型和目標類型之間進行轉換。this
public interface GenericConverter { //返回這個GenericConverter可以轉換的原類型和目標類型的組合 Set<ConvertiblePair> getConvertibleTypes(); //用於進行類型轉換 Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); }
其中ConvertiblePair
是一對原類型和目標類型的封裝。.net
假設咱們有個需求是用person的id或fullname轉換成對應的Person對象,能夠經過下面的code
GenericConverter
實現。
package com.demo.mvc.component; import java.util.HashSet; import java.util.Set; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.GenericConverter; import com.demo.domain.Person; import com.demo.service.PersonService; public class PersonGenericConverter implements GenericConverter { @Autowired private PersonService personService; @Override public Set<ConvertiblePair> getConvertibleTypes() { // 構建原類型和目標類型對 Set<ConvertiblePair> pairs = new HashSet<ConvertiblePair>(); pairs.add(new ConvertiblePair(Integer.class, Person.class)); pairs.add(new ConvertiblePair(String.class, Person.class)); return pairs; } @Override public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { if (source == null) { return null; } Person person = null; if (sourceType.getType() == Integer.class) { // 根據id查詢person person = personService.findPersonById((Integer) source); } else if (sourceType.getType() == String.class) { // 根據fullname查詢person person = personService.findPersonByFullname((String) source); } return person; } }
在getConvertibleTypes
方法中添加了兩組轉換的組合,Integer到Person和String到Person。而後咱們給PersonGenericConverter注入了一個PersonService,在convert
方法根據參數的類型來決定查詢的方法。
GenericConverter
還有一個子類接口org.springframework.core.convert.converter.ConditionalGenericConverter
它另外繼承了ConditionalConverter
public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter { }
public interface ConditionalConverter { boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType); }
顧名思義就是多了matches
方法用於條件判斷,使得轉換器不只在知足類型匹配時能夠轉換,還要知足此條件。看一下Spring內置的StringToArrayConverter
就好理解了,它實現了ConditionalGenericConverter
。在字符串轉換到數組的過程當中,不只要原類型爲String,目標類型是數組,還有目標類型數組的元素類型也要匹配。
final class StringToArrayConverter implements ConditionalGenericConverter { private final ConversionService conversionService; public StringToArrayConverter(ConversionService conversionService) { this.conversionService = conversionService; } @Override public Set<ConvertiblePair> getConvertibleTypes() { return Collections.singleton(new ConvertiblePair(String.class, Object[].class)); } @Override public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { //此處還需判斷數組的元素類型是否匹配 return this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor()); } @Override public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { if (source == null) { return null; } String string = (String) source; String[] fields = StringUtils.commaDelimitedListToStringArray(string); Object target = Array.newInstance(targetType.getElementTypeDescriptor().getType(), fields.length); for (int i = 0; i < fields.length; i++) { String sourceElement = fields[i]; Object targetElement = this.conversionService.convert(sourceElement.trim(), sourceType, targetType.getElementTypeDescriptor()); Array.set(target, i, targetElement); } return target; } }
友情連接