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
プロパティを設定します。プロパティ値は、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 の章の変換とフォーマットを参照してください。
特定の状況では、変換中にフォーマットを適用したい場合があります。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
に変換する特定のコンバーターを作成する必要はありません。