Spring 類型轉換
core.convert
套件提供了一般的類型轉換系統。該系統定義了 SPI 來實作類型轉換邏輯,以及 API 來在執行階段執行類型轉換。在 Spring 容器中,您可以將此系統作為 PropertyEditor
實作的替代方案,以將外部化的 Bean 屬性值字串轉換為所需的屬性類型。您也可以在應用程式中任何需要類型轉換的地方使用公用 API。
Converter SPI
實作類型轉換邏輯的 SPI 很簡單且強型別,如下列介面定義所示
package org.springframework.core.convert.converter;
public interface Converter<S, T> {
T convert(S source);
}
若要建立自己的轉換器,請實作 Converter
介面,並將 S
參數化為您要轉換的來源類型,將 T
參數化為您要轉換成的目標類型。如果 S
的集合或陣列需要轉換為 T
的陣列或集合,您也可以透明地套用此類轉換器,前提是委派陣列或集合轉換器也已註冊 (DefaultConversionService
預設會執行此操作)。
對於每次呼叫 convert(S)
,保證來源引數不會為 null。如果轉換失敗,您的 Converter
可能會擲回任何未檢查的例外狀況。特別是,它應該擲回 IllegalArgumentException
以報告無效的來源值。請注意確保您的 Converter
實作是執行緒安全的。
為了方便起見,core.convert.support
套件中提供了數個轉換器實作。這些包括從字串到數字和其他常見類型的轉換器。以下清單顯示了 StringToInteger
類別,這是一個典型的 Converter
實作
package org.springframework.core.convert.support;
final class StringToInteger implements Converter<String, Integer> {
public Integer convert(String source) {
return Integer.valueOf(source);
}
}
使用 ConverterFactory
當您需要集中整個類別階層的轉換邏輯時 (例如,從 String
轉換為 Enum
物件時),您可以實作 ConverterFactory
,如下列範例所示
package org.springframework.core.convert.converter;
public interface ConverterFactory<S, R> {
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
將 S 參數化為您要轉換的來源類型,R 參數化為定義您可以轉換成的類別範圍的基礎類型。然後實作 getConverter(Class<T>)
,其中 T 是 R 的子類別。
以 StringToEnumConverterFactory
為例
package org.springframework.core.convert.support;
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToEnumConverter(targetType);
}
private final class StringToEnumConverter<T extends Enum> implements Converter<String, T> {
private Class<T> enumType;
public StringToEnumConverter(Class<T> enumType) {
this.enumType = enumType;
}
public T convert(String source) {
return (T) Enum.valueOf(this.enumType, source.trim());
}
}
}
使用 GenericConverter
當您需要複雜的 Converter
實作時,請考慮使用 GenericConverter
介面。GenericConverter
的簽章比 Converter
更彈性但類型安全性較低,它支援在多個來源類型和目標類型之間進行轉換。此外,GenericConverter
提供來源和目標欄位 Context,您可以在實作轉換邏輯時使用。此類 Context 允許類型轉換由欄位註解或欄位簽章上宣告的泛型資訊驅動。以下清單顯示了 GenericConverter
的介面定義
package org.springframework.core.convert.converter;
public interface GenericConverter {
public Set<ConvertiblePair> getConvertibleTypes();
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}
若要實作 GenericConverter
,請讓 getConvertibleTypes()
傳回支援的來源→目標類型配對。然後實作 convert(Object, TypeDescriptor, TypeDescriptor)
以包含您的轉換邏輯。來源 TypeDescriptor
提供對持有要轉換之值的來源欄位的存取權。目標 TypeDescriptor
提供對要設定轉換後值的目標欄位的存取權。
GenericConverter
的一個好範例是在 Java 陣列和集合之間進行轉換的轉換器。此類 ArrayToCollectionConverter
會內省宣告目標集合類型的欄位,以解析集合的元素類型。這可讓來源陣列中的每個元素在集合設定在目標欄位上之前,都轉換為集合元素類型。
由於 GenericConverter 是更複雜的 SPI 介面,因此您應僅在需要時才使用它。對於基本類型轉換需求,請優先使用 Converter 或 ConverterFactory 。 |
使用 ConditionalGenericConverter
有時,您希望 Converter
僅在特定條件成立時才執行。例如,您可能希望僅在目標欄位上存在特定註解時才執行 Converter
,或者您可能希望僅在目標類別上定義了特定方法 (例如 static valueOf
方法) 時才執行 Converter
。ConditionalGenericConverter
是 GenericConverter
和 ConditionalConverter
介面的聯集,可讓您定義此類自訂比對準則
public interface ConditionalConverter {
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}
ConditionalGenericConverter
的一個好範例是 IdToEntityConverter
,它在持久實體識別碼和實體參考之間進行轉換。此類 IdToEntityConverter
可能僅在目標實體類型宣告靜態尋找器方法 (例如 findAccount(Long)
) 時才比對。您可能會在 matches(TypeDescriptor, TypeDescriptor)
的實作中執行此類尋找器方法檢查。
ConversionService
API
ConversionService
定義了在執行階段執行類型轉換邏輯的統一 API。轉換器通常在下列外觀介面之後執行
package org.springframework.core.convert;
public interface ConversionService {
boolean canConvert(Class<?> sourceType, Class<?> targetType);
<T> T convert(Object source, Class<T> targetType);
boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}
大多數 ConversionService
實作也實作了 ConverterRegistry
,後者提供了用於註冊轉換器的 SPI。在內部,ConversionService
實作委派給其註冊的轉換器以執行類型轉換邏輯。
core.convert.support
套件中提供了穩健的 ConversionService
實作。GenericConversionService
是適用於大多數環境的通用實作。ConversionServiceFactory
提供了方便的 Factory,用於建立常見的 ConversionService
組態。
組態 ConversionService
ConversionService
是一個無狀態物件,旨在於應用程式啟動時實例化,然後在多個執行緒之間共用。在 Spring 應用程式中,您通常為每個 Spring 容器 (或 ApplicationContext
) 組態一個 ConversionService
實例。Spring 會擷取該 ConversionService
,並在框架需要執行類型轉換時使用它。您也可以將此 ConversionService
注入到您的任何 Bean 中並直接呼叫它。
如果沒有向 Spring 註冊 ConversionService ,則會使用原始的基於 PropertyEditor 的系統。 |
若要向 Spring 註冊預設 ConversionService
,請新增下列 Bean 定義,並將 id
設定為 conversionService
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean"/>
預設 ConversionService
可以在字串、數字、列舉、集合、Map 和其他常見類型之間進行轉換。若要使用您自己的自訂轉換器來補充或覆寫預設轉換器,請設定 converters
屬性。屬性值可以實作 Converter
、ConverterFactory
或 GenericConverter
介面中的任何一個。
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="example.MyCustomConverter"/>
</set>
</property>
</bean>
在 Spring MVC 應用程式中使用 ConversionService
也很常見。請參閱 Spring MVC 章節中的 轉換和格式化。
在某些情況下,您可能希望在轉換期間套用格式化。請參閱 FormatterRegistry
SPI,以了解有關使用 FormattingConversionServiceFactoryBean
的詳細資訊。
以程式設計方式使用 ConversionService
若要以程式設計方式使用 ConversionService
實例,您可以像對待任何其他 Bean 一樣注入對它的參考。以下範例顯示了如何執行此操作
-
Java
-
Kotlin
@Service
public class MyService {
private final ConversionService conversionService;
public MyService(ConversionService conversionService) {
this.conversionService = conversionService;
}
public void doIt() {
this.conversionService.convert(...)
}
}
@Service
class MyService(private val conversionService: ConversionService) {
fun doIt() {
conversionService.convert(...)
}
}
對於大多數使用案例,您可以使用指定 targetType
的 convert
方法,但它不適用於更複雜的類型,例如參數化元素的集合。例如,如果您想要以程式設計方式將 Integer
的 List
轉換為 String
的 List
,則需要提供來源類型和目標類型的正式定義。
幸運的是,TypeDescriptor
提供了各種選項來使執行此操作變得簡單明瞭,如下列範例所示
-
Java
-
Kotlin
DefaultConversionService cs = new DefaultConversionService();
List<Integer> input = ...
cs.convert(input,
TypeDescriptor.forObject(input), // List<Integer> type descriptor
TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)));
val cs = DefaultConversionService()
val input: List<Integer> = ...
cs.convert(input,
TypeDescriptor.forObject(input), // List<Integer> type descriptor
TypeDescriptor.collection(List::class.java, TypeDescriptor.valueOf(String::class.java)))
請注意,DefaultConversionService
會自動註冊適用於大多數環境的轉換器。這包括集合轉換器、純量轉換器和基本的 Object
-to-String
轉換器。您可以使用 DefaultConversionService
類別上的靜態 addDefaultConverters
方法,向任何 ConverterRegistry
註冊相同的轉換器。
值類型的轉換器會重複用於陣列和集合,因此無需建立特定的轉換器來從 S
的 Collection
轉換為 T
的 Collection
,假設標準集合處理是適當的。