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
,然後呼叫Converter
的convert
方法,這個後面再說。
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 提供了三種類型的轉換器(Converter
、ConverterFactory
、GenericConverter
),分別用來處理 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
先看GenericConversionService
的removeConvertible
方法:
@Override
public void removeConvertible(Class<?> sourceType, Class<?> targetType) {
//直接呼叫Converters的remove方法,移除轉換型別對
this.converters.remove(sourceType, targetType);
invalidateCache();
}
再看Converters
的remove
方法:直接呼叫原生的map的remove方法,從map中移除指定的ConvertiblePair
key,這樣也就會把該key對應的所有轉換器物件移除。
public void remove(Class<?> sourceType, Class<?> targetType) {
this.converters.remove(new ConvertiblePair(sourceType, targetType));
}
canConvert和convert
接下來,重頭戲來了。我們先看GenericConversionService
對canConvert
的實現:
//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);
}
再看Converters
的find
方法:
@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 ...
以此類推,直至找到對應的型別轉換器。
再看Converters
的getRegisteredConverter
方法,看它是怎麼找到已經註冊的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;
}
再看ConvertersForPair
的getConverter
方法:
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;
}
//其他方法省略
}
至此GenericConversionService
的getConverter
方法呼叫結束。
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
方法:方法很簡單,就是直接呼叫找到的GenericConverter
的convert
方法進行型別轉換。
@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));
}
剛開始,就會判斷是否有conversionService
bean名稱的ConversionService.class
,如果有,就建立ConversionService
的例項,並設定到beanFactory
中。
所以如果我們想要在spring中使用ConversionService
,只需要註冊到spring容器中就行了。
此外Spring提供了一個ConversionServiceFactoryBean
的FactoryBean
,這樣會方便我們的使用:
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,將會於下一篇介紹。