1. 程式人生 > 其它 >Spring原始碼分析:型別轉換(一)之PropertyEditor和ConversionService

Spring原始碼分析:型別轉換(一)之PropertyEditor和ConversionService

引子

建立Person類:

@Data
@Component
public class Person {

	@Value("wj")
	private String name;

	@Value("20")
	private Integer age;

	@Value("2020/10/10 12:30:30")
	private Date birth;

	@Value("java,html")
	private String[] subject;

	@Value("C:\\SoftPlugin.dll")
	private File file;

	@Override
	public String toString() {
		return "Person{" +
				"name='" + name + '\'' +
				", age=" + age +
				", birth=" + birth +
				", subject=" + Arrays.toString(subject) +
				", file=" + file +
				'}';
	}
}

建立配置類:

@ComponentScan("com.wj.spring2")
@Configuration
public class MainConfig {
}

main方法:

	public static void main(String[] args){
		ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
		Person bean = applicationContext.getBean(Person.class);
		System.out.println(bean);
	}

執行結果:

控制檯最後列印了Person類的相關屬性,發現@Value中的配置的內容會被解析到對應的欄位上,String型別的name被設定進去還說的通,因為@Value中寫的內容本來就是String型別的,那麼為什麼Integer、Date、String[]、File型別的欄位,只要符合一定的格式,就能被正確解析,Spring底層是怎麼做的,用了什麼元件?我怎麼對型別轉換進行擴充套件?

留著問題慢慢往下看!!!

Spring型別轉換的幾種方式

PropertyEditor

PropertyEditor:屬性編輯器,java.beans包下面的,它規定了將外部設定值轉換為內部JavaBean屬性值的轉換介面方法。PropertyEditor主要的介面方法說明如下:

  • Object getValue():返回屬性的當前值。基本型別被封裝成對應的包裝類例項;
  • void setValue(Object newValue):設定屬性的值,基本型別以包裝類傳入(自動裝箱);
  • String getAsText():將屬性物件用一個字串表示,以便外部的屬性編輯器能以視覺化的方式顯示。預設返回null,表示該屬性不能以字串表示;
  • void setAsText(String text):用一個字串去更新屬性的內部值,這個字串一般從外部屬性編輯器傳入;
  • String[] getTags():返回表示有效屬性值的字串陣列(如boolean屬性對應的有效Tag為true和false),以便屬性編輯器能以下拉框的方式顯示出來。預設返回null,表示屬性沒有匹配的字元值有限集合;
  • String getJavaInitializationString():為屬性提供一個表示初始值的字串,屬性編輯器以此值作為屬性的預設值。
  • void addPropertyChangeListener(PropertyChangeListener listener):新增屬性改變監聽器
  • void removePropertyChangeListener(PropertyChangeListener listener): 移除屬性改變監聽器

Java為PropertyEditor提供了一個方便的實現類:PropertyEditorSupport,該類實現了PropertyEditor介面並提供預設實現,一般情況下,使用者可以通過擴充套件這個方便類設計自己的屬性編輯器。

下面我們簡單實現一個屬性編輯器,用於將String型別轉換成Date型別:

public class DatePropertyEditor extends PropertyEditorSupport {

	private static final String[] parsePatterns = {"yyyy-MM-dd","yyyy年MM月dd日",
			"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy/MM/dd",
			"yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyyMMdd"};


	@Override
	public void setAsText(String text) throws IllegalArgumentException {
		try {
            //org.apache.commons.lang3.time.DateUtils
			Date date = DateUtils.parseDate(text, parsePatterns);
			setValue(date);
		} catch (ParseException e) {
			e.printStackTrace();
		}
	}
}

測試:

	public static void main(String[] args) {
		PropertyEditor editor = new DatePropertyEditor();
		editor.setAsText("2021-10-10 12:00:01");
		System.out.println(editor.getValue());
	}

輸出結果:

現在我們如果將前面Person類中的Date值改掉:

	@Value("2020-10-10 12:30:30")
	private Date birth;

再執行就會報錯:

為什麼會出現這種情況?這個我們稍後解釋。

但是為了解決這種問題,spring給我們提供了一種增加自定義PropertyEditor的方式:在配置類中增加如下bean

	@Bean
	CustomEditorConfigurer customEditorConfigurer() {
		CustomEditorConfigurer configurer = new CustomEditorConfigurer();
		Map<Class<?>, Class<? extends PropertyEditor>> customEditorsMap = new HashMap<>();
        //表示DatePropertyEditor可以將String型別轉換成Date型別
        //spring中,如果發現輸入型別是String,輸出型別是Date,那麼就會使用該PropertyEditor
		customEditorsMap.put(Date.class, DatePropertyEditor.class);
		configurer.setCustomEditors(customEditorsMap);
		return configurer;
	}

再執行就執行成功了:

這說明我們自定義的屬性編輯器生效了,把字串轉換成Date型別。

但是,jdk提供的PropertyEditor有很大的侷限性,只能將String型別轉換成其他型別,但是對於Object轉成Object型別就沒有辦法了,所以Spring提供了強大的型別轉換服務ConversionService供我們使用。

三種轉換器

Converter<S, T> (1:1轉換)

介面原始碼:

@FunctionalInterface
public interface Converter<S, T> {

    /**
    * 轉換方法:將S型別轉換成T
    */
	@Nullable
	T convert(S source);

    /**
    * 支援鏈式呼叫
    */
	default <U> Converter<S, U> andThen(Converter<? super T, ? extends U> after) {
		Assert.notNull(after, "After Converter must not be null");
		return (S s) -> {
			T initialResult = convert(s);
			return (initialResult != null ? after.convert(initialResult) : null);
		};
	}

}

Converter介面用於將S型別轉換成T型別。

下面對它進行測試:我們可以用Spring提供的StringToBooleanConverter,可以將String轉換成Boolean型別。

        ConversionService conversionService = new DefaultConversionService();
        Boolean flag = conversionService.convert("1", Boolean.class);
        System.out.println(flag);

測試結果:

???不是說Converter嗎,怎麼用的是ConversionService

其實我們建立了一個DefaultConversionService,它內部建立了很多Converter的實現。然後我們呼叫conversionService.convert時,它內部去匹配Converter,然後呼叫Converterconvert方法,這個後面再說。

ConverterFactory<S, R> (1:N轉換)

原始碼如下:

public interface ConverterFactory<S, R> {

    //R 的子類都可以統一由這個 ConverterFactory 進行轉換。
	<T extends R> Converter<S, T> getConverter(Class<T> targetType);

}

測試:

		System.out.println(conversionService.convert(1.2, Long.class));
		System.out.println(conversionService.convert(1.2, Double.class));

這裡,conversionService底層使用了NumberToNumberConverterFactory類去轉換:

final class NumberToNumberConverterFactory implements ConverterFactory<Number, Number>, ConditionalConverter {

	@Override
	public <T extends Number> Converter<Number, T> getConverter(Class<T> targetType) {
		return new NumberToNumber<>(targetType);
	}

	@Override
	public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
		return !sourceType.equals(targetType);
	}


	private static final class NumberToNumber<T extends Number> implements Converter<Number, T> {

		private final Class<T> targetType;

		NumberToNumber(Class<T> targetType) {
			this.targetType = targetType;
		}

		@Override
		public T convert(Number source) {
            //NumberUtils.convertNumberToTargetClass方法去轉換
			return NumberUtils.convertNumberToTargetClass(source, this.targetType);
		}
	}

}

GenericConverter (N:N轉換)

GenericConverter一般都是和ConditionalConverter一起使用的。

原始碼如下:

public interface GenericConverter {

    //獲取當前GenericConverter的source==>target的轉換對
	@Nullable
	Set<ConvertiblePair> getConvertibleTypes();

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

	/**
	* 用於儲存 sourc===>target 的轉換對
	*/
	final class ConvertiblePair {

		private final Class<?> sourceType;

		private final Class<?> targetType;

		public ConvertiblePair(Class<?> sourceType, Class<?> targetType) {
			Assert.notNull(sourceType, "Source type must not be null");
			Assert.notNull(targetType, "Target type must not be null");
			this.sourceType = sourceType;
			this.targetType = targetType;
		}
		//其他方法省略
	}
}

public interface ConditionalConverter {
	//判斷當前converter能否從sourceType轉換成targetType
	boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
//繼承了GenericConverter和ConditionalConverter
public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}

測試:我們可以通過CollectionToCollectionConverter將List<String>轉換成Set<Integer>:

        ConversionService conversionService = new DefaultConversionService();
        Set<Integer> set = (Set<Integer>)conversionService.convert(Arrays.asList("1","2","1"),
                TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)),
                TypeDescriptor.collection(Set.class, TypeDescriptor.valueOf(Integer.class)));
        System.out.println(set);

執行結果:

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
	@Nullable
	public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        //如果要轉換的source為null,則直接返回
		if (source == null) {
			return null;
		}
        //將source強轉為Collection
		Collection<?> sourceCollection = (Collection<?>) source;

		// Shortcut if possible...
        //判斷targetType集合型別與sourceType集合型別是否相同
		boolean copyRequired = !targetType.getType().isInstance(source);
		if (!copyRequired && sourceCollection.isEmpty()) {
            //不用轉換,直接返回
			return source;
		}
        
		TypeDescriptor elementDesc = targetType.getElementTypeDescriptor();
        //判斷集合元素型別是否設定了,如果沒有則為Object型別,且集合型別沒有變
		if (elementDesc == null && !copyRequired) {
            //不用轉換,直接返回
			return source;
		}

		// At this point, we need a collection copy in any case, even if just for finding out about element copies...
        //建立原集合大小的空集合
		Collection<Object> target = CollectionFactory.createCollection(targetType.getType(),
				(elementDesc != null ? elementDesc.getType() : null), sourceCollection.size());
		//如果沒有設定集合元素型別
		if (elementDesc == null) {
            //直接addAll
			target.addAll(sourceCollection);
		}
		else {
            //遍歷原集合的所有元素,依次呼叫conversionService.convert()方法,將sourceElement轉換成targetElement
            //並將轉換結果放到新集合中
			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);
	}

}

ConversionService

Spring 3.0 提供了三種類型的轉換器(ConverterConverterFactoryGenericConverter),分別用來處理 1:1、1:N、N:N 的型別轉換。而ConversionService是來統一管理所有的型別轉換器,負責註冊、查詢、轉換等功能,統一對外提供服務。

檢視該類實現:

ConversionService還有一個FormattingConversionService的實現,因為它跟Formatter有關,不再本文討論之內,這裡不再介紹。

  • ConverterRegistry:轉換器註冊中心。負責轉換器的註冊、刪除

  • ConversionService:統一的型別轉換服務。屬於面向開發者使用的門面介面

  • ConfigurableConversionService:上兩個介面的組合介面

  • GenericConversionService 實現了 ConfigurableConversionService 介面,Spring 使用的 ConversionService 都是基於這個類的擴充套件。

  • DefaultConversionService 擴充套件 GenericConversionService,註冊了一批預設的轉換器。

GenericConversionService

save

GenericConversionService 實現了 ConversionService, ConverterRegistry 兩個介面的功能,上面提到的 DefaultConversionService 就是基於 GenericConversionService 的擴充套件,只是註冊了一些預設的轉換器。

接下來:先介紹GenericConversionService的增加轉換器方法:

	//管理所有已經註冊在ConversionService的Converter
	private final Converters converters = new Converters();

	@Override
	public void addConverter(Converter<?, ?> converter) {
		//獲取傳進來的Converter的兩個泛型型別
		ResolvableType[] typeInfo = getRequiredTypeInfo(converter.getClass(), Converter.class);
		if (typeInfo == null && converter instanceof DecoratingProxy) {
			typeInfo = getRequiredTypeInfo(((DecoratingProxy) converter).getDecoratedClass(), Converter.class);
		}
		if (typeInfo == null) {
			throw new IllegalArgumentException("Unable to determine source type <S> and target type <T> for your " +
					"Converter [" + converter.getClass().getName() + "]; does the class parameterize those types?");
		}
		//將Converter轉成Converter的介面卡, 該ConverterAdapter實現ConditionalGenericConverter介面(支援N:N轉換)
		addConverter(new ConverterAdapter(converter, typeInfo[0], typeInfo[1]));
	}

	//將明確指定source/target的型別對新增到ConverterRegistry
	//允許將 Converter 重用於多個不同的對,而無需為每對建立 Converter 類。
	@Override
	public <S, T> void addConverter(Class<S> sourceType, Class<T> targetType, Converter<? super S, ? extends T> converter) {
		addConverter(new ConverterAdapter(
				converter, ResolvableType.forClass(sourceType), ResolvableType.forClass(targetType)));
	}

	//增加ConverterFactory支援1:N
	@Override
	public void addConverterFactory(ConverterFactory<?, ?> factory) {
        //獲取傳進來的ConverterFactory的兩個泛型型別
		ResolvableType[] typeInfo = getRequiredTypeInfo(factory.getClass(), ConverterFactory.class);
		if (typeInfo == null && factory instanceof DecoratingProxy) {
			typeInfo = getRequiredTypeInfo(((DecoratingProxy) factory).getDecoratedClass(), ConverterFactory.class);
		}
		if (typeInfo == null) {
			throw new IllegalArgumentException("Unable to determine source type <S> and target type <T> for your " +
					"ConverterFactory [" + factory.getClass().getName() + "]; does the class parameterize those types?");
		}
		//將ConverterFactory轉成ConverterFactory介面卡,該介面卡實現ConditionalGenericConverter介面(支援N:N轉換)
		addConverter(new ConverterFactoryAdapter(factory,
				new ConvertiblePair(typeInfo[0].toClass(), typeInfo[1].toClass())));
	}

	//增加GenericConverter
	@Override
	public void addConverter(GenericConverter converter) {
		this.converters.add(converter);
		invalidateCache();
	}

可以看到,當增加Converter或者是ConverterFactory,ConversionService都會先建立該轉換器的介面卡,實現了ConditionalGenericConverter介面,然後呼叫addConverter(GenericConverter converter)方法,確保無論是新增哪種轉換器,最終在ConversionService中註冊的都是GenericConverter,巧妙如斯!!

最終呼叫this.converters.add(converter);,converters是GenericConversionService類的屬性,是Converters類:

	private static class Converters {

		private final Set<GenericConverter> globalConverters = new CopyOnWriteArraySet<>();

		private final Map<ConvertiblePair, ConvertersForPair> converters = new ConcurrentHashMap<>(256);

		public void add(GenericConverter converter) {
			//獲取該converter的source/target型別對
			Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes();
			if (convertibleTypes == null) {
				Assert.state(converter instanceof ConditionalConverter,
						"Only conditional converters may return null convertible types");
                //如果型別對為null,新增到globalConverters的集合中
				this.globalConverters.add(converter);
			}
			else {
                //遍歷convertibleTypes
				for (ConvertiblePair convertiblePair : convertibleTypes) {
					//getMatchableConverters(convertiblePair)返回ConvertersForPair後,呼叫它的add方法,增加converter
					getMatchableConverters(convertiblePair).add(converter);
				}
			}
		}

		//從converters根據convertiblePair獲取ConvertersForPair,如果沒有就建立一個空的ConvertiblePair---ConvertersForPair鍵值對
		private ConvertersForPair getMatchableConverters(ConvertiblePair convertiblePair) {
			return this.converters.computeIfAbsent(convertiblePair, k -> new ConvertersForPair());
		}
        
        //其他方法省略。。

再看ConvertersForPair類:

	/**
	 * Manages converters registered with a specific {@link ConvertiblePair}.
	 */
	private static class ConvertersForPair {

        //真正存放GenericConverter的地方
		private final Deque<GenericConverter> converters = new ConcurrentLinkedDeque<>();

		//把converter放到隊首,所以越後add的GenericConverter越在前面
		public void add(GenericConverter converter) {
			this.converters.addFirst(converter);
		}

		//其他方法省略
	}

所以最終儲存結構是這樣:(當然了,這只是簡化版的,GenericConversionService實際上比這個還要複雜一點)

remove

先看GenericConversionServiceremoveConvertible方法:

	@Override
	public void removeConvertible(Class<?> sourceType, Class<?> targetType) {
        //直接呼叫Converters的remove方法,移除轉換型別對
		this.converters.remove(sourceType, targetType);
		invalidateCache();
	}

再看Convertersremove方法:直接呼叫原生的map的remove方法,從map中移除指定的ConvertiblePair key,這樣也就會把該key對應的所有轉換器物件移除。

		public void remove(Class<?> sourceType, Class<?> targetType) {
			this.converters.remove(new ConvertiblePair(sourceType, targetType));
		}

canConvert和convert

接下來,重頭戲來了。我們先看GenericConversionServicecanConvert的實現:

	//canConvert(@Nullable Class<?> sourceType, Class<?> targetType)呼叫的是該類的同名過載方法
	@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");
        //如果sourceType等於null,直接返回true
		if (sourceType == null) {
			return true;
		}
        //呼叫getConverter方法,如果能得到converter,就返回true
		GenericConverter converter = getConverter(sourceType, targetType);
		return (converter != null);
	}

再看getConverter方法:

	private static final GenericConverter NO_MATCH = new NoOpConverter("NO_MATCH");	
	private static final GenericConverter NO_OP_CONVERTER = new NoOpConverter("NO_OP");

	@Nullable
	protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
        //建立了source/target型別對的快取key,這一次getConverter的結果會被快取起來,後面再次呼叫getConverter,直接返回結果,提升查詢的速度
		ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
        //先從快取中查詢快取key對應的GenericConverter
		GenericConverter converter = this.converterCache.get(key);
		if (converter != null) {
            //不等於null,判斷該Converter是否是NO_MATCH,如果是,返回null,否則直接返回converter
            //這裡處理就有點向redis的快取穿透問題一樣,為不存在的key也設定一個空物件
			return (converter != NO_MATCH ? converter : null);
		}
		//如果converter為null,說明快取中沒有,則是第一次查詢,所以直接從Converters中查詢
        //呼叫find方法
		converter = this.converters.find(sourceType, targetType);
		if (converter == null) {
            //獲取預設的Converter
			converter = getDefaultConverter(sourceType, targetType);
		}
		//不等於null
		if (converter != null) {
            //快取這次查詢的結果,並返回
			this.converterCache.put(key, converter);
			return converter;
		}
		//快取一個空的converter
		this.converterCache.put(key, NO_MATCH);
        //返回null
		return null;
	}

	//如果源型別可分配給目標型別,則返回 NO_OP 轉換器。
	//否則返回 null,表示找不到合適的轉換器。
	@Nullable
	protected GenericConverter getDefaultConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
		return (sourceType.isAssignableTo(targetType) ? NO_OP_CONVERTER : null);
	}

再看Convertersfind方法:

		@Nullable
		public GenericConverter find(TypeDescriptor sourceType, TypeDescriptor targetType) {
			// Search the full type hierarchy
            //獲取source型別的類層級和target型別的類層級
			List<Class<?>> sourceCandidates = getClassHierarchy(sourceType.getType());
			List<Class<?>> targetCandidates = getClassHierarchy(targetType.getType());
            //雙重for迴圈遍歷
			for (Class<?> sourceCandidate : sourceCandidates) {
				for (Class<?> targetCandidate : targetCandidates) {
                    //建立source型別/target型別一個convertiblePair
					ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);
                    //呼叫getRegisteredConverter()查詢GenericConverter
					GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair);
					if (converter != null) {
                        //如果找到了,直接返回converter
						return converter;
					}
				}
			}
            //沒有找到,返回null
			return null;
		}

這裡的getClassHierarchy返回型別的類層級是什麼意思?

Double型別為例,我來解釋一下:

Double類的繼承實現關係如下:

那麼Double呼叫此方法,會返回它自己、它的父類和它的父介面:

最終返回[Class<Double>, Class<Number>, Class<Comparable>, Class<Serialzable>, Class<Object>],不要忘記Object型別,它也是Double的父類。

getClassHierarchy說完了,那麼這裡的雙重迴圈是怎麼回事?

無論source型別還是taget型別都有類層級,他會每兩個一對依次去查。

以Double和Integer為例,查詢順序為:Double<>Integer 到 Double<>Number 到 Double<>Comparable ... Number<>Integer 到 Number<==>Number ...

以此類推,直至找到對應的型別轉換器。

再看ConvertersgetRegisteredConverter方法,看它是怎麼找到已經註冊的Converter:

		//從此方法來看:converters的優先順序要比globalConverters高
		@Nullable
		private GenericConverter getRegisteredConverter(TypeDescriptor sourceType,
				TypeDescriptor targetType, ConvertiblePair convertiblePair) {

			// Check specifically registered converters
			//直接從map中呼叫get(convertiblePair),找到convertersForPair
			ConvertersForPair convertersForPair = this.converters.get(convertiblePair);
			if (convertersForPair != null) {
				//從convertersForPair中找到Converter
				GenericConverter converter = convertersForPair.getConverter(sourceType, targetType);
				if (converter != null) {
					//找到,直接返回
					return converter;
				}
			}
			// Check ConditionalConverters for a dynamic match
			//如果converters的Map集合中找不到,就從globalConverters中找
			for (GenericConverter globalConverter : this.globalConverters) {
				//依次呼叫matches方法
				if (((ConditionalConverter) globalConverter).matches(sourceType, targetType)) {
					//找到就直接返回
					return globalConverter;
				}
			}
			//找不到返回null
			return null;
		}

再看ConvertersForPairgetConverter方法:

	private static class ConvertersForPair {

		private final Deque<GenericConverter> converters = new ConcurrentLinkedDeque<>();

		@Nullable
		public GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
			//遍歷佇列
			for (GenericConverter converter : this.converters) {
				//如果converter實現了ConditionalGenericConverter介面們則會呼叫matches方法,看是否匹配
				//如果沒有實現,則直接返回
				if (!(converter instanceof ConditionalGenericConverter) ||
						((ConditionalGenericConverter) converter).matches(sourceType, targetType)) {
					return converter;
				}
			}
			return null;
		}
        //其他方法省略
	}

至此GenericConversionServicegetConverter方法呼叫結束。

getConverter流程圖如下:圖有點亂,畢竟判斷比較多,最好還是自己斷點走一遍。

canConvert看完後,再看convert方法:

	@Override
	@Nullable
	public Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
        //一些前置判斷
		Assert.notNull(targetType, "Target type to convert to cannot be null");
		if (sourceType == null) {
			Assert.isTrue(source == null, "Source must be [null] if source type == [null]");
			return handleResult(null, targetType, convertNullSource(null, targetType));
		}
		if (source != null && !sourceType.getObjectType().isInstance(source)) {
			throw new IllegalArgumentException("Source to convert from must be an instance of [" +
					sourceType + "]; instead it was a [" + source.getClass().getName() + "]");
		}
        //呼叫getConvert方法,與上面分析的getConverter方法是同一個方法
		GenericConverter converter = getConverter(sourceType, targetType);
		if (converter != null) {
            //呼叫ConversionUtils的invokeConverter方法
			Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
            //handleResult:處理返回值
			return handleResult(sourceType, targetType, result);
		}
        //呼叫handleConverterNotFound方法
		return handleConverterNotFound(source, sourceType, targetType);
	}

先看invokeConverter方法:方法很簡單,就是直接呼叫找到的GenericConverterconvert方法進行型別轉換。

	@Nullable
	public static Object invokeConverter(GenericConverter converter, @Nullable Object source,
			TypeDescriptor sourceType, TypeDescriptor targetType) {

		try {
			return converter.convert(source, sourceType, targetType);
		}
		catch (ConversionFailedException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new ConversionFailedException(sourceType, targetType, source, ex);
		}
	}

handleResult方法也容易,就是判斷result==null時,看targetType是否是原始型別,如果是,直接丟擲異常

	@Nullable
	private Object handleResult(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType, @Nullable Object result) {
		if (result == null) {
			assertNotPrimitiveTargetType(sourceType, targetType);
		}
        //返回最終結果
		return result;
	}

再看handleConverterNotFound方法:

	@Nullable
	private Object handleConverterNotFound(
			@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {

		if (source == null) {
			//判斷targetType是否是原始型別
			assertNotPrimitiveTargetType(sourceType, targetType);
			return null;
		}
		//當source型別為null或source型別可分配給target型別並且判斷給定的source是否是target型別的例項
		//如果判斷返回true,則直接返回source
		if ((sourceType == null || sourceType.isAssignableTo(targetType)) &&
				targetType.getObjectType().isInstance(source)) {
			return source;
		}
		//丟擲異常
		throw new ConverterNotFoundException(sourceType, targetType);
	}

至此,GenericConversionService類基本分析完畢。

DefaultConversionService

接下來看GenericConversionService類的子類DefaultConversionService

//預設的型別轉換服務
public class DefaultConversionService extends GenericConversionService {

	@Nullable
	private static volatile DefaultConversionService sharedInstance;

	//構造方法
	public DefaultConversionService() {
		addDefaultConverters(this);
	}

	//返回一個共享例項,此外使用雙重檢查鎖保證了sharedInstance例項只會被建立一次
	public static ConversionService getSharedInstance() {
		DefaultConversionService cs = sharedInstance;
		if (cs == null) {
			synchronized (DefaultConversionService.class) {
				cs = sharedInstance;
				if (cs == null) {
                    //呼叫構造方法
					cs = new DefaultConversionService();
					sharedInstance = cs;
				}
			}
		}
		return cs;
	}

	//增加一些預設的轉換器
	public static void addDefaultConverters(ConverterRegistry converterRegistry) {
		addScalarConverters(converterRegistry);
		addCollectionConverters(converterRegistry);

		converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
		converterRegistry.addConverter(new StringToTimeZoneConverter());
		converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
		converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());

		converterRegistry.addConverter(new ObjectToObjectConverter());
		converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
		converterRegistry.addConverter(new FallbackObjectToStringConverter());
		converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
	}

	public static void addCollectionConverters(ConverterRegistry converterRegistry) {
		ConversionService conversionService = (ConversionService) converterRegistry;

		converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService));
		converterRegistry.addConverter(new CollectionToArrayConverter(conversionService));

		converterRegistry.addConverter(new ArrayToArrayConverter(conversionService));
		converterRegistry.addConverter(new CollectionToCollectionConverter(conversionService));
		converterRegistry.addConverter(new MapToMapConverter(conversionService));

		converterRegistry.addConverter(new ArrayToStringConverter(conversionService));
		converterRegistry.addConverter(new StringToArrayConverter(conversionService));

		converterRegistry.addConverter(new ArrayToObjectConverter(conversionService));
		converterRegistry.addConverter(new ObjectToArrayConverter(conversionService));

		converterRegistry.addConverter(new CollectionToStringConverter(conversionService));
		converterRegistry.addConverter(new StringToCollectionConverter(conversionService));

		converterRegistry.addConverter(new CollectionToObjectConverter(conversionService));
		converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));

		converterRegistry.addConverter(new StreamConverter(conversionService));
	}

	private static void addScalarConverters(ConverterRegistry converterRegistry) {
		converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());

		converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
		converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToCharacterConverter());
		converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new NumberToCharacterConverter());
		converterRegistry.addConverterFactory(new CharacterToNumberFactory());

		converterRegistry.addConverter(new StringToBooleanConverter());
		converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
		converterRegistry.addConverter(new EnumToStringConverter((ConversionService) converterRegistry));

		converterRegistry.addConverterFactory(new IntegerToEnumConverterFactory());
		converterRegistry.addConverter(new EnumToIntegerConverter((ConversionService) converterRegistry));

		converterRegistry.addConverter(new StringToLocaleConverter());
		converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToCharsetConverter());
		converterRegistry.addConverter(Charset.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToCurrencyConverter());
		converterRegistry.addConverter(Currency.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToPropertiesConverter());
		converterRegistry.addConverter(new PropertiesToStringConverter());

		converterRegistry.addConverter(new StringToUUIDConverter());
		converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter());
	}

}

可以看到,當我們new出一個DefaultConversionService例項時,它內部會註冊很多的不同功能的Converter

Spring中註冊自定義型別轉換器

關於型別轉換服務,在refresh容器重新整理方法的finishBeanFactoryInitialization這一步中:

	protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		// Initialize conversion service for this context.
		//初始化ConversionService
		//預設情況下是沒有ConversionService的
		if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
				beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
			beanFactory.setConversionService(
					beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
		}

剛開始,就會判斷是否有conversionServicebean名稱的ConversionService.class,如果有,就建立ConversionService的例項,並設定到beanFactory中。

所以如果我們想要在spring中使用ConversionService,只需要註冊到spring容器中就行了。

此外Spring提供了一個ConversionServiceFactoryBeanFactoryBean,這樣會方便我們的使用:

public class ConversionServiceFactoryBean implements FactoryBean<ConversionService>, InitializingBean {

	@Nullable
	private Set<?> converters;

	@Nullable
	private GenericConversionService conversionService;

	//此方法用於我們傳入自定義的型別轉換器
	public void setConverters(Set<?> converters) {
		this.converters = converters;
	}

	@Override
	public void afterPropertiesSet() {
        //呼叫createConversionService方法
		this.conversionService = createConversionService();
        //把converters註冊到conversionService中
		ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
	}

	//建立一個DefaultConversionService的例項
	protected GenericConversionService createConversionService() {
		return new DefaultConversionService();
	}

	@Override
	@Nullable
	public ConversionService getObject() {
		return this.conversionService;
	}

	@Override
	public Class<? extends ConversionService> getObjectType() {
		return GenericConversionService.class;
	}

	@Override
	public boolean isSingleton() {
		return true;
	}

}

下面提供一個配置的示例:

@ComponentScan("com.wj.spring2")
@Configuration
public class MainConfig {

//	@Bean
//	CustomEditorConfigurer customEditorConfigurer() {
//		CustomEditorConfigurer configurer = new CustomEditorConfigurer();
//		Map<Class<?>, Class<? extends PropertyEditor>> customEditorsMap = new HashMap<>();
//		customEditorsMap.put(Date.class, DatePropertyEditor.class);
//		configurer.setCustomEditors(customEditorsMap);
//		return configurer;
//	}

    //注意這裡方法名必須是conversionService,因為spring裡面規定了,不然不會使用這個conversionService
    //此外我們自定義的型別轉換器會優先與DefaultConversionService中預設的型別轉換器
	//前面解釋過了,底層維護的是一個佇列,並且呼叫的addFirst方法
	@Bean
	ConversionServiceFactoryBean conversionService() {
		ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean();
		Set<Converter<?, ?>> set =  new HashSet<>();
		set.add(new StringToDateConverter());
		conversionServiceFactoryBean.setConverters(set);
		return conversionServiceFactoryBean;
	}

	private static class StringToDateConverter implements Converter<String, Date>{

		private final java.lang.String[] parsePatterns = {"yyyy-MM-dd","yyyy年MM月dd日",
				"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy/MM/dd",
				"yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyyMMdd"};

		@Override
		public Date convert(String source) {
			try {
				return DateUtils.parseDate(source, parsePatterns);
			} catch (ParseException e) {
				e.printStackTrace();
			}
			return null;
		}
	}
}

此外,spring還有一種型別轉換方式:TypeConverter,將會於下一篇介紹。