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>),其中 TR 的子類。

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 介面。GenericConverterConverter 具有更靈活但型別不那麼強的簽名,支援在多種源型別和目標型別之間進行轉換。此外,GenericConverter 還提供了源和目標型別描述符,您可以在實現轉換邏輯時使用這些描述符。此類型別描述符使型別轉換可以透過描述符源(例如欄位或方法)上的註解或欄位簽名、方法簽名等中宣告的泛型資訊來驅動。以下清單顯示了 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 介面,所以您應該只在需要時才使用它。對於基本的型別轉換需求,優先使用 ConverterConverterFactory

使用 ConditionalGenericConverter

有時,您希望 Converter 僅在特定條件成立時執行。例如,您可能希望僅當目標欄位或方法上存在特定註解時才執行 Converter,或者您可能希望僅當目標型別上定義了特定方法(例如 static valueOf 方法)時才執行 ConverterConditionalGenericConverterGenericConverterConditionalConverter 介面的聯合,它允許您定義此類自定義匹配條件

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 提供了一個方便的工廠來建立常見的 ConversionService 配置。

配置 ConversionService

ConversionService 是一個無狀態物件,旨在在應用程式啟動時例項化,然後由多個執行緒共享。在 Spring 應用程式中,您通常為每個 Spring 容器(或 ApplicationContext)配置一個 ConversionService 例項。Spring 會獲取該 ConversionService 並在框架需要執行型別轉換時使用它。您還可以將此 ConversionService 注入到您的任何 bean 中並直接呼叫它。

如果沒有 ConversionService 註冊到 Spring,則使用原始的基於 PropertyEditor 的系統。

要將預設的 ConversionService 註冊到 Spring,請新增以下 bean 定義,其 idconversionService

<bean id="conversionService"
	class="org.springframework.context.support.ConversionServiceFactoryBean"/>

預設的 ConversionService 可以在字串、數字、列舉、集合、對映和其他常見型別之間進行轉換。要用您自己的自定義轉換器補充或覆蓋預設轉換器,請設定 converters 屬性。屬性值可以實現 ConverterConverterFactoryGenericConverter 介面中的任何一個。

<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 章中的 轉換和格式化

在某些情況下,您可能希望在轉換期間應用格式化。有關使用 FormattingConversionServiceFactoryBean 的詳細資訊,請參閱 FormatterRegistry SPI

以程式設計方式使用 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(...)
	}
}

對於大多數用例,您可以使用指定 targetTypeconvert 方法,但它不適用於更復雜的型別,例如引數化元素的集合。例如,如果您想以程式設計方式將 IntegerList 轉換為 StringList,您需要提供源型別和目標型別的正式定義。

幸運的是,TypeDescriptor 提供了各種選項,使其變得簡單明瞭,如以下示例所示

  • Java

  • Kotlin

DefaultConversionService cs = new DefaultConversionService();

List<Integer> input = ...
cs.convert(input,
	TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Integer.class)), (1)
	TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class))); (2)
1 List<Integer> 型別描述符
2 List<String> 型別描述符
val cs = DefaultConversionService()

val input: List<Integer> = ...
cs.convert(input,
	TypeDescriptor.collection(List::class.java, TypeDescriptor.valueOf(Integer::class.java)), (1)
	TypeDescriptor.collection(List::class.java, TypeDescriptor.valueOf(String::class.java))) (2)
1 List<Integer> 型別描述符
2 List<String> 型別描述符

請注意,DefaultConversionService 會自動註冊適用於大多數環境的轉換器。這包括集合轉換器、標量轉換器和基本的 Object-to-String 轉換器。您可以使用 DefaultConversionService 類上的靜態 addDefaultConverters 方法將相同的轉換器註冊到任何 ConverterRegistry

值型別的轉換器被重用於陣列和集合,因此假設標準集合處理是合適的,則無需建立特定的轉換器來將 SCollection 轉換為 TCollection

© . This site is unofficial and not affiliated with VMware.