Spring 型変換

core.convert パッケージは、一般的な型変換システムを提供します。システムは、型変換ロジックを実装する SPI と、実行時に型変換を実行する API を定義します。Spring コンテナー内では、このシステムを PropertyEditor 実装の代替として使用して、外部化された Bean プロパティ値文字列を必要なプロパティ型に変換できます。また、型変換が必要なアプリケーションのどこでもパブリック API を使用できます。

コンバーター 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 パッケージには、便宜上いくつかのコンバーター実装が提供されています。これらには、文字列から数値およびその他の一般的な型へのコンバーターが含まれます。次のリストは、典型的な Converter 実装である StringToInteger クラスを示しています。

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 インターフェースの使用を検討してください。Converter よりも柔軟性がありますが、型付けがそれほど強力ではない署名では、GenericConverter は複数のソース型とターゲット型間の変換をサポートします。さらに、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 インターフェースであるため、必要な場合にのみ使用してください。基本的な型変換のニーズには、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 の良い例は、永続エンティティ ID とエンティティ参照の間で変換する 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 実装は、コンバーターを登録するための SPI を提供する ConverterRegistry も実装します。内部的に、ConversionService 実装は、登録されたコンバーターに委譲して、型変換ロジックを実行します。

ConversionService 実装は、core.convert.support パッケージで提供されます。GenericConversionService は、ほとんどの環境での使用に適した汎用実装です。ConversionServiceFactory は、一般的な ConversionService 構成を作成するための便利なファクトリを提供します。

ConversionService の構成

ConversionService は、アプリケーションの起動時にインスタンス化され、複数のスレッド間で共有されるように設計されたステートレスオブジェクトです。Spring アプリケーションでは、通常、各 Spring コンテナー(または ApplicationContext)に対して ConversionService インスタンスを構成します。Spring は、その ConversionService を取得し、フレームワークが型変換を実行する必要がある場合にそれを使用します。この ConversionService を任意の Bean に注入して、直接呼び出すこともできます。

ConversionService が Spring に登録されていない場合、元の PropertyEditor ベースのシステムが使用されます。

デフォルト ConversionService を Spring に登録するには、conversionService の id で次の Bean 定義を追加します。

<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(...)
	}
}

ほとんどのユースケースでは、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 から String コンバーターが含まれます。DefaultConversionService クラスの静的 addDefaultConverters メソッドを使用して、同じコンバーターを任意の ConverterRegistry に登録できます。

値型のコンバーターは配列およびコレクションに再利用されるため、標準のコレクション処理が適切であると仮定すると、S の Collection から T の Collection に変換する特定のコンバーターを作成する必要はありません。