Spring 擴展之深入分析 Bean 的類型轉換體系

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們知道不管 bean 對象裏面的屬性時什麼類型,他們都是通過 XML 、Properties 或者其他方式來配置這些屬性對象類型的。在 Spring 容器加載過程中,這些屬性都是以 String 類型加載進容器的,但是最終都需要將這些 String 類型的屬性轉換 Bean 對象屬性所對應真正的類型,要想完成這種由字符串到具體對象的轉換,就需要這種轉換規則相關的信息,而這些信息以及轉換過程由 Spring 類型轉換體系來完成。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們依然以 xml 爲例,在 Spring 容器加載階段,容器將 xml 文件中定義的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}},{"type":"text","text":" 解析爲 BeanDefinition,BeanDefinition 中存儲着我們定義一個 bean 需要的所有信息,包括屬性,這些屬性是以 String 類型的存儲的。當用戶觸發 Bean 實例化階段時,Spring 容器會將這些屬性轉換爲這些屬性真正對應的類型。我們知道在 bean 實例化階段,屬性的注入是在實例化 bean 階段的屬性注入階段,即 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"populateBean()","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法。在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"populateBean()","attrs":{}}],"attrs":{}},{"type":"text","text":" 中會將 BeanDefinition 中定義的屬性值翻譯爲 PropertyValue 然後調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"applyPropertyValues()","attrs":{}}],"attrs":{}},{"type":"text","text":" 進行屬性應用。其中 PropertyValue 用於保存單個 bean 屬性的信息和值的對象。在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"applyPropertyValues()","attrs":{}}],"attrs":{}},{"type":"text","text":" 中會調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"convertForProperty()","attrs":{}}],"attrs":{}},{"type":"text","text":" 進行屬性轉換,如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private Object convertForProperty(\n @Nullable Object value, String propertyName, BeanWrapper bw, TypeConverter converter) {\n\n if (converter instanceof BeanWrapperImpl) {\n return ((BeanWrapperImpl) converter).convertForProperty(value, propertyName);\n }\n else {\n PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);\n MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);\n return converter.convertIfNecessary(value, pd.getPropertyType(), methodParam);\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"若 TypeConverter 爲 BeanWrapperImpl 類型,則使用 BeanWrapperImpl 來進行類型轉換,這裏主要是因爲 BeanWrapperImpl 實現了 PropertyEditorRegistry 接口。否則則調用 TypeConverter 的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"convertIfNecessary()","attrs":{}}],"attrs":{}},{"type":"text","text":" 進行類型轉換。TypeConverter 是定義類型轉換方法的接口,通常情況下與 PropertyEditorRegistry 配合使用實現類型轉換。關於 BeanWrapperImpl 小編後續專門出文分析它。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"convertIfNecessary()","attrs":{}}],"attrs":{}},{"type":"text","text":" 的實現者有兩個:DataBinder 和 TypeConverterSupport ,其中 DataBinder 主要用於參數綁定(熟悉 Spring MVC 的都應該知道這個類),TypeConverterSupport 則是 TypeConverter 的基本實現,使用的是 package-private 策略。 所以這裏我們只需要關注 TypeConverterSupport 的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"convertIfNecessary()","attrs":{}}],"attrs":{}},{"type":"text","text":",如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public T convertIfNecessary(@Nullable Object value, @Nullable Class requiredType, @Nullable MethodParameter methodParam)\n throws TypeMismatchException {\n\n return doConvert(value, requiredType, methodParam, null);\n}\n\nprivate T doConvert(@Nullable Object value,@Nullable Class requiredType,\n @Nullable MethodParameter methodParam, @Nullable Field field) throws TypeMismatchException {\n\n Assert.state(this.typeConverterDelegate != null, \"No TypeConverterDelegate\");\n try {\n if (field != null) {\n return this.typeConverterDelegate.convertIfNecessary(value, requiredType, field);\n }\n else {\n return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);\n }\n }\n catch (ConverterNotFoundException | IllegalStateException ex) {\n throw new ConversionNotSupportedException(value, requiredType, ex);\n }\n catch (ConversionException | IllegalArgumentException ex) {\n throw new TypeMismatchException(value, requiredType, ex);\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們一直往下跟會跟蹤到 TypeConverterDelegate 的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"convertIfNecessary()","attrs":{}}],"attrs":{}},{"type":"text","text":" ,會發現如下代碼段:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/66/66353a9c50e322e965648beda62e74cf.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果沒有自定義的編輯器則使用 ConversionService 。ConversionService 是 Spring 自 3 後推出來用來替代 PropertyEditor 轉換模式的轉換體系,接口定義如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public interface ConversionService {\n\n boolean canConvert(@Nullable Class> sourceType, Class> targetType);\n\n boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType);\n\n @Nullable\n T convert(@Nullable Object source, Class targetType);\n\n @Nullable\n Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其 UML 類圖如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/5d/5dae2df55357fdf3f3e5a3c7470e5c6b.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"ConfigurableConversionService","attrs":{}},{"type":"text","text":":ConversionService 的配置接口,繼承 ConversionService 和 ConverterRegistry 兩個接口,用於合併他們兩者的操作,以便於通過 add 和 remove 的方式添加和刪除轉換器。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"GenericConversionService","attrs":{}},{"type":"text","text":":ConversionService 接口的基礎實現,適用於大部分條件下的轉換工作,通過 ConfigurableConversionService 接口間接地將 ConverterRegistry 實現爲註冊 API 。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"DefaultConversionService","attrs":{}},{"type":"text","text":":ConversionService 接口的默認實現,適用於大部分條件下的轉換工作。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"迴歸到 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"convertIfNecessary()","attrs":{}}],"attrs":{}},{"type":"text","text":",在該方法中如果沒有自定義的屬性編輯器則調用 ConversionService 接口的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"convert()","attrs":{}}],"attrs":{}},{"type":"text","text":",方法定義如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"source:要轉換的源對象,可以爲 null","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"sourceType:source 的類型的上下文,如果 source 爲 null,則可以爲 null","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"targetType:source 要轉換的類型的上下文。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"convert()","attrs":{}}],"attrs":{}},{"type":"text","text":" 將給定的源對象 source 轉換爲指定的 targetType。TypeDescriptors 提供有關發生轉換的源位置和目標位置的附加上下文,通常是對象字段或屬性位置。方法由子類 GenericConversionService 實現:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {\n // 刪掉 if ,其實就是上面的 null 判斷\n GenericConverter converter = getConverter(sourceType, targetType);\n if (converter != null) {\n Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);\n return handleResult(sourceType, targetType, result);\n }\n return handleConverterNotFound(source, sourceType, targetType);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先根據 sourceType 和 targetType 調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"getConverter()","attrs":{}}],"attrs":{}},{"type":"text","text":" 獲取 GenericConverter 對象 converter ,如果 converter 爲 null,則調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"handleConverterNotFound()","attrs":{}}],"attrs":{}},{"type":"text","text":",否則調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"handleResult()","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法。","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"getConverter()","attrs":{}}],"attrs":{}},{"type":"text","text":" 如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {\n ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);\n GenericConverter converter = this.converterCache.get(key);\n if (converter != null) {\n return (converter != NO_MATCH ? converter : null);\n }\n\n converter = this.converters.find(sourceType, targetType);\n if (converter == null) {\n converter = getDefaultConverter(sourceType, targetType);\n }\n\n if (converter != null) {\n this.converterCache.put(key, converter);\n return converter;\n }\n\n this.converterCache.put(key, NO_MATCH);\n return null;\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這段代碼意圖非常明確,從 converterCache 緩存中獲取,如果存在返回,否則從 converters 中獲取,然後加入到 converterCache 緩存中。converterCache 和 converters 是 GenericConversionService 維護的兩個很重要的對象,其中 converterCache 用於存儲 GenericConverter ,converters 對象爲 GenericConversionService 的內部類。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private final Converters converters = new Converters();\nprivate final Map converterCache = new ConcurrentReferenceHashMap<>(64);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Converters 用於管理所有註冊的轉換器,其內部維護一個 Set 和 Map 的數據結構用於管理轉換器,如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private final Set globalConverters = new LinkedHashSet<>();\n\nprivate final Map converters = new LinkedHashMap<>(36);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同時提供了相應的方法(如 add、remove)操作這兩個集合。在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"getConverter()","attrs":{}}],"attrs":{}},{"type":"text","text":" 中如果緩存 converterCache 中 不存在,則調用 Converters 對象的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"find()","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法獲取相應的 GenericConverter,如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public GenericConverter find(TypeDescriptor sourceType, TypeDescriptor targetType) {\n // Search the full type hierarchy\n List> sourceCandidates = getClassHierarchy(sourceType.getType());\n List> targetCandidates = getClassHierarchy(targetType.getType());\n for (Class> sourceCandidate : sourceCandidates) {\n for (Class> targetCandidate : targetCandidates) {\n ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);\n GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair);\n if (converter != null) {\n return converter;\n }\n }\n }\n return null;\n}\n\nprivate GenericConverter getRegisteredConverter(TypeDescriptor sourceType,\n TypeDescriptor targetType, ConvertiblePair convertiblePair) {\n\n // Check specifically registered converters\n ConvertersForPair convertersForPair = this.converters.get(convertiblePair);\n if (convertersForPair != null) {\n GenericConverter converter = convertersForPair.getConverter(sourceType, targetType);\n if (converter != null) {\n return converter;\n }\n }\n // Check ConditionalConverters for a dynamic match\n for (GenericConverter globalConverter : this.globalConverters) {\n if (((ConditionalConverter) globalConverter).matches(sourceType, targetType)) {\n return globalConverter;\n }\n }\n return null;\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"find()","attrs":{}}],"attrs":{}},{"type":"text","text":" 中會根據 sourceType 和 targetType 去查詢 Converters 中維護的 Map 中是否包括支持的註冊類型,如果存在返回 GenericConverter ,如果沒有存在返回 null。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當得到 GenericConverter 後,則調用其 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"convert()","attrs":{}}],"attrs":{}},{"type":"text","text":" 進行類型轉換。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"到這裏我們就可以得到 bean 屬性定義的真正類型了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"GenericConverter 接口","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"GenericConverter 是一個轉換接口,一個用於在兩種或更多種類型之間轉換的通用型轉換器接口。它是 Converter SPI 體系中最靈活的,也是最複雜的接口,靈活性在於 GenericConverter 可以支持在多個源/目標類型對之間進行轉換,同時也可以在類型轉換過程中訪問源/目標字段上下文。由於該接口足夠複雜,所有當更簡單的 Converter 或 ConverterFactory 接口足夠使用時,通常不應使用此接口。其定義如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public interface GenericConverter {\n\n @Nullable\n Set getConvertibleTypes();\n\n @Nullable\n Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"GenericConverter 的子類有這麼多(看類名就知道是幹嘛的了):","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ae/ae851c788e7011fd6e325056cab2f797.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們看一個子類的實現 StringToArrayConverter,該子類將逗號分隔的 String 轉換爲 Array。如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {\n if (source == null) {\n return null;\n }\n String string = (String) source;\n String[] fields = StringUtils.commaDelimitedListToStringArray(string);\n TypeDescriptor targetElementType = targetType.getElementTypeDescriptor();\n Assert.state(targetElementType != null, \"No target element type\");\n Object target = Array.newInstance(targetElementType.getType(), fields.length);\n for (int i = 0; i < fields.length; i++) {\n String sourceElement = fields[i];\n Object targetElement = this.conversionService.convert(sourceElement.trim(), sourceType, targetElementType);\n Array.set(target, i, targetElement);\n }\n return target;\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在類型轉換體系中,Spring 提供了非常多的類型轉換器,除了上面的 GenericConverter,還有 Converter、ConditionalConverter、ConverterFactory。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Converter","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Converter 是一個將 S 類型的源對象轉換爲 T 類型的目標對象的轉換器。該接口是線程安全的,所以可以共享。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public interface Converter {\n @Nullable\n T convert(S source);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"子類如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/78/7846fde6784243ff50c21366a438dab6.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"ConditionalConverter","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ConditionalConverter 接口用於表示有條件的類型轉換,通過轉入的sourceType 與 targetType 判斷轉換能否匹配,只有可匹配的轉換纔會調用convert 方法進行轉換,如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public interface ConditionalConverter {\n boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ConditionalConverter 的子類如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8a/8a7276c5d8bcdd7a724e69147655a710.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"ConverterFactory","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個用於“遠程”轉換的轉換工廠,可以將對象從 S 轉換爲 R 的子類型。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public interface ConverterFactory {\n Converter getConverter(Class targetType);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"子類如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/63/63de40b54f1bc3a032275065d43916a1.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"四種不同的轉換器承載着不同的轉換過程:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Converter:用於 1:1 的 source -> target 類型轉換","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ConverterFactory:用於 1:N 的 source -> target 類型轉換","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"GenericConverter用於 N:N 的 source -> target 類型轉換","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ConditionalConverter:有條件的 source -> target 類型轉換","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"GenericConversionService","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"轉換器介紹完了,我們再次迴歸到 ConversionService 接口中去,該接口定義了兩類方法 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"canConvert()","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"convert()","attrs":{}}],"attrs":{}},{"type":"text","text":",其中 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"canConvert()","attrs":{}}],"attrs":{}},{"type":"text","text":" 用於判 sourceType 能否轉成 targetType ,而 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"convert()","attrs":{}}],"attrs":{}},{"type":"text","text":" 用於將 source 轉成轉入的 TargetType 類型實例。這兩類方法都是在 GenericConversionService 中實現。類 GenericConversionService 實現 ConfigurableConversionService 接口,而 ConfigurableConversionService 接口繼承 ConversionService 和 ConverterRegistry。ConverterRegistry 提供了類型轉換器的管理功能,他提供了四個 add 和 一個 remove 方法,支持註冊/刪除相應的類型轉換器。GenericConversionService 作爲一個基礎實現類,它即支持了不同類型之間的轉換,也對各類型轉換器進行管理,主要是通過一個 Map 類型的 converterCache 和一個內部類 Converters。在上面已經分析了 GenericConversionService 執行類型轉換的過程 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"cover()","attrs":{}}],"attrs":{}},{"type":"text","text":",下面我們就一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"addConverter()","attrs":{}}],"attrs":{}},{"type":"text","text":" 來看看它是如何完成轉換器的注入工作的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public void addConverter(Converter, ?> converter) {\n ResolvableType[] typeInfo = getRequiredTypeInfo(converter.getClass(), Converter.class);\n if (typeInfo == null && converter instanceof DecoratingProxy) {\n typeInfo = getRequiredTypeInfo(((DecoratingProxy) converter).getDecoratedClass(), Converter.class);\n }\n if (typeInfo == null) {\n throw new IllegalArgumentException(\"Unable to determine source type and target type for your \" +\n \"Converter [\" + converter.getClass().getName() + \"]; does the class parameterize those types?\");\n }\n addConverter(new ConverterAdapter(converter, typeInfo[0], typeInfo[1]));\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先根據 converter 獲取 ResolvableType,然後將其與 converter 封裝成一個 ConverterAdapter 實例,最後調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"addConverter()","attrs":{}}],"attrs":{}},{"type":"text","text":"。ResolvableType 用於封裝 Java 的類型。ConverterAdapter 則是 Converter 的一個適配器, 它實現了 GenericConverter 和 ConditionalConverter 兩個類型轉換器。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"addConverter()","attrs":{}}],"attrs":{}},{"type":"text","text":" 如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public void addConverter(GenericConverter converter) {\n this.converters.add(converter);\n invalidateCache();\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"直接調用內部類 Converters 的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"add()","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法,如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public void add(GenericConverter converter) {\n Set convertibleTypes = converter.getConvertibleTypes();\n if (convertibleTypes == null) {\n Assert.state(converter instanceof ConditionalConverter,\n \"Only conditional converters may return null convertible types\");\n this.globalConverters.add(converter);\n }\n else {\n for (ConvertiblePair convertiblePair : convertibleTypes) {\n ConvertersForPair convertersForPair = getMatchableConverters(convertiblePair);\n convertersForPair.add(converter);\n }\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"getConvertibleTypes()","attrs":{}}],"attrs":{}},{"type":"text","text":" 獲取 ConvertiblePair 集合,如果爲空,則加入到 globalConverters 集合中,否則通過迭代的方式依次添加。ConvertiblePair 爲 source-to-targer 的持有者,它持有 source 和 target 的 class 類型,如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"final class ConvertiblePair {\n private final Class> sourceType;\n private final Class> targetType;\n\n // 其他代碼 \n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在迭代過程中會根據 ConvertiblePair 獲取相應的 ConvertersForPair ,然後 converter 轉換器加入其中,ConvertiblePair 用於管理使用特定GenericConverter.ConvertiblePair 註冊的轉換器。如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private static class ConvertersForPair {\n\n private final LinkedList converters = new LinkedList<>();\n\n public void add(GenericConverter converter) {\n this.converters.addFirst(converter);\n }\n\n @Nullable\n public GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {\n for (GenericConverter converter : this.converters) {\n if (!(converter instanceof ConditionalGenericConverter) ||\n ((ConditionalGenericConverter) converter).matches(sourceType, targetType)) {\n return converter;\n }\n }\n return null;\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實內部就是維護一個 LinkedList 集合。他內部有兩個方法:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"add()","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"getConverter()","attrs":{}}],"attrs":{}},{"type":"text","text":",實現較爲簡單,這裏就不多介紹了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"DefaultConversionService","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DefaultConversionService 是 ConversionService 的默認實現,它繼承 GenericConversionService,GenericConversionService 主要用於轉換器的註冊和調用,DefaultConversionService 則是爲 ConversionService 體系提供一些默認的轉換器。在 DefaultConversionService 構造方法中就會添加默認的 Converter ,如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public DefaultConversionService() {\n addDefaultConverters(this);\n}\n\n public static void addDefaultConverters(ConverterRegistry converterRegistry) {\n addScalarConverters(converterRegistry);\n addCollectionConverters(converterRegistry);\n\n converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));\n if (jsr310Available) {\n Jsr310ConverterRegistrar.registerJsr310Converters(converterRegistry);\n }\n\n converterRegistry.addConverter(new ObjectToObjectConverter());\n converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));\n converterRegistry.addConverter(new FallbackObjectToStringConverter());\n if (javaUtilOptionalClassAvailable) {\n converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當然它還提供了一些其他的方法如 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"addCollectionConverters()","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"addScalarConverters()","attrs":{}}],"attrs":{}},{"type":"text","text":" 用於註冊其他類型的轉換器。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"至此,從 bean 屬性的轉換,到 Spring ConversionService 體系的轉換器 Converter 以及轉換器的管理都介紹完畢了,下篇我們將分析如何利用 ConversionService 實現自定義類型轉換器。","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章