Transformer
メッセージトランスフォーマーは、メッセージプロデューサーとメッセージコンシューマーの疎結合を可能にする上で非常に重要なロールを果たします。すべてのメッセージ生成コンポーネントに、次のコンシューマーがどの型を期待するかを知る必要はなく、それらのコンポーネント間にトランスフォーマーを追加できます。String
を XML ドキュメントに変換するような汎用トランスフォーマーも非常に再利用可能です。
一部のシステムでは、標準的なデータモデル (英語) を提供するのが最善かもしれませんが、Spring Integration の一般的な哲学は、特定の形式を必要としないことです。むしろ、最大限の柔軟性を得るために、Spring Integration は、拡張のために可能な限り単純なモデルを提供することを目指しています。他のエンドポイント型と同様に、XML または Java アノテーションで宣言型構成を使用すると、単純な POJO をメッセージトランスフォーマーのロールに適合させることができます。この章の残りの部分では、これらの構成オプションについて説明します。
柔軟性を最大化するために、Spring は XML ベースのメッセージペイロードを必要としません。それでも、フレームワークは、XML ベースのペイロードを処理するための便利なトランスフォーマーを提供します。これが実際にアプリケーションにとって正しい選択である場合です。これらのトランスフォーマーの詳細については、XML サポート - XML ペイロードの処理を参照してください。 |
XML を使用した Transformer の構成
<transformer>
要素は、メッセージ変換エンドポイントを作成するために使用されます。input-channel
および output-channel
属性に加えて、ref
属性が必要です。ref
は、単一のメソッドで @Transformer
アノテーションを含むオブジェクトを指す場合もあれば(アノテーション付きの Transformer の構成を参照)、method
属性で指定された明示的なメソッド名の値と組み合わせる場合もあります。
<int:transformer id="testTransformer" ref="testTransformerBean" input-channel="inChannel"
method="transform" output-channel="outChannel"/>
<beans:bean id="testTransformerBean" class="org.foo.TestTransformer" />
通常、カスタムトランスフォーマハンドラーの実装を他の <transformer>
定義で再利用できる場合は、ref
属性を使用することをお勧めします。ただし、カスタムトランスフォーマーハンドラーの実装を <transformer>
の単一の定義にスコープする必要がある場合、次の例に示すように、内部 Bean 定義を定義できます。
<int:transformer id="testTransformer" input-channel="inChannel" method="transform"
output-channel="outChannel">
<beans:bean class="org.foo.TestTransformer"/>
</transformer>
同じ <transformer> 構成で ref 属性と内部ハンドラー定義の両方を使用することは許可されません。曖昧な状態を作成し、例外がスローされるためです。 |
ref 属性が AbstractMessageProducingHandler を継承する Bean(フレームワーク自体によって提供されるトランスフォーマーなど)を参照する場合、出力チャネルをハンドラーに直接注入することにより、構成が最適化されます。この場合、各 ref は、個別の Bean インスタンス(または prototype -scoped Bean)にするか、内部 <bean/> 構成型を使用する必要があります。誤って複数の Bean から同じメッセージハンドラーを参照すると、構成例外が発生します。 |
POJO を使用する場合、変換に使用されるメソッドは、受信メッセージの Message
型またはペイロード型のいずれかを予期する場合があります。また、@Header
および @Headers
パラメーターアノテーションをそれぞれ使用して、メッセージヘッダー値を個別に、または完全なマップとして受け入れることもできます。メソッドの戻り値は任意の型にできます。戻り値自体が Message
である場合、トランスフォーマーの出力チャネルに渡されます。
Spring Integration 2.0 以降、メッセージトランスフォーマーの変換メソッドは null
を返すことができなくなりました。メッセージトランスフォーマは常に各ソースメッセージを有効なターゲットメッセージに変換する必要があるため、null
を返すと例外が発生します。つまり、専用の <filter>
オプションがあるため、メッセージトランスフォーマーをメッセージフィルターとして使用しないでください。ただし、この型の動作が必要な場合(コンポーネントが null
を返す可能性があり、エラーとは見なされない場合)、サービスアクティベーターを使用できます。その requires-reply
値はデフォルトで false
ですが、トランスフォーマーと同様に、true
に設定して、null
戻り値に対して例外をスローすることができます。
Transformers と Spring 式言語 (SpEL)
ルーター、アグリゲーター、その他のコンポーネントと同様に、Spring Integration 2.0 以降、トランスフォーマーも変換ロジックが比較的単純な場合はいつでも SpEL サポートの恩恵を受けることができます。次の例は、SpEL 式の使用方法を示しています。
<int:transformer input-channel="inChannel"
output-channel="outChannel"
expression="payload.toUpperCase() + '- [' + T(System).currentTimeMillis() + ']'"/>
前の例では、カスタムトランスフォーマーを作成せずにペイロードを変換します。ペイロード(String
と想定)は大文字で、現在のタイムスタンプと連結され、いくつかのフォーマットが適用されています。
共通 Transformers
Spring Integration は、いくつかのトランスフォーマー実装を提供します。
オブジェクトから文字列への Transformer
Object
の toString()
表現を使用することはかなり一般的であるため、Spring Integration は ObjectToStringTransformer
を提供し、その出力は String payload
を持つ Message
です。この String
は、受信メッセージのペイロードで toString()
操作を呼び出した結果です。次の例は、オブジェクトから文字列への変換のインスタンスを宣言する方法を示しています。
<int:object-to-string-transformer input-channel="in" output-channel="out"/>
このトランスフォーマーの潜在的な用途は、file
名前空間の「送信チャネルアダプター」に任意のオブジェクトを送信することです。そのチャネルアダプターは、デフォルトで String
、バイト配列、java.io.File
ペイロードのみをサポートしますが、アダプターが必要な変換を処理する直前にこのトランスフォーマを追加します。toString()
呼び出しの結果がファイルに書き込みたいものである限り、うまく機能します。それ以外の場合は、前に示した汎用 'transformer' 要素を使用して、カスタム POJO ベースのトランスフォーマーを提供できます。
logging-channel-adapter はメッセージペイロードをログに記録できるため、デバッグ時には、このトランスフォーマーは通常必要ありません。詳細については、ワイヤータップを参照してください。 |
オブジェクトから文字列への変換は非常に簡単です。受信ペイロードで
より高度な処理(実行時の文字セットの動的な選択など)を行うには、次の例に示すように、代わりに SpEL 式ベースのトランスフォーマーを使用できます。
|
Object
をバイト配列に直列化する必要がある場合、またはバイト配列を逆直列化して Object
に戻す必要がある場合、Spring Integration は対称直列化トランスフォーマーを提供します。これらはデフォルトで標準の Java 直列化を使用しますが、serializer
属性と deserializer
属性をそれぞれ使用して、Spring Serializer
または Deserializer
戦略の実装を提供できます。次の例は、Spring のシリアライザーとデシリアライザーを使用する方法を示しています。
<int:payload-serializing-transformer input-channel="objectsIn" output-channel="bytesOut"/>
<int:payload-deserializing-transformer input-channel="bytesIn" output-channel="objectsOut"
allow-list="com.mycom.*,com.yourcom.*"/>
信頼できないソースからデータをデシリアライズするときは、パッケージおよびクラスパターンの allow-list を追加することを検討する必要があります。デフォルトでは、すべてのクラスはデシリアライズされます。 |
Object
-to- Map
および Map
-to- Object
トランス
Spring Integration は、Object
から Map
へのトランスフォーマーと Map
から Object
へのトランスフォーマーも提供します。これらは、JSON を使用してオブジェクトグラフを直列化および逆直列化します。オブジェクト階層は、最もプリミティブな型(String
、int
など)にイントロスペクトされます。この型へのパスは、変換された Map
で key
になる SpEL で記述されます。プリミティブ型が値になります。
次の例を考えてみましょう。
public class Parent{
private Child child;
private String name;
// setters and getters are omitted
}
public class Child{
private String name;
private List<String> nickNames;
// setters and getters are omitted
}
前の例の 2 つのクラスは、次の Map
に変換されます。
{person.name=George, person.child.name=Jenna, person.child.nickNames[0]=Jen ...}
JSON ベースの Map
では、実際の型を共有せずにオブジェクト構造を記述できます。これにより、構造を維持している限り、オブジェクトグラフを異なる型のオブジェクトグラフに復元および再構築できます。
例: Map
-to- Object
トランスフォーマーを使用して、前述の構造を次のオブジェクトグラフに復元できます。
public class Father {
private Kid child;
private String name;
// setters and getters are omitted
}
public class Kid {
private String name;
private List<String> nickNames;
// setters and getters are omitted
}
「構造化された」マップを作成する必要がある場合は、flatten
属性を指定できます。デフォルトは "true" です。'false' に設定すると、構造は Map
オブジェクトの Map
になります。
次の例を考えてみましょう。
public class Parent {
private Child child;
private String name;
// setters and getters are omitted
}
public class Child {
private String name;
private List<String> nickNames;
// setters and getters are omitted
}
前の例の 2 つのクラスは、次の Map
に変換されます。
{name=George, child={name=Jenna, nickNames=[Bimbo, ...]}}
これらのトランスフォーマーを構成するために、Spring Integration は、次の例に示すように、Object-to-Map のネームスペースサポートを提供します。
<int:object-to-map-transformer input-channel="directInput" output-channel="output"/>
次のように、flatten
属性を false に設定することもできます。
<int:object-to-map-transformer input-channel="directInput" output-channel="output" flatten="false"/>
Spring Integration は、次の例に示すように、Map-to-Object のネームスペースサポートを提供します。
<int:map-to-object-transformer input-channel="input"
output-channel="output"
type="org.something.Person"/>
または、次の例に示すように、ref
属性とプロトタイプスコープの Bean を使用することもできます。
<int:map-to-object-transformer input-channel="inputA"
output-channel="outputA"
ref="person"/>
<bean id="person" class="org.something.Person" scope="prototype"/>
"ref" および "type" 属性は相互に排他的です。また、'ref' 属性を使用する場合、「プロトタイプ」スコープの Bean を指す必要があります。そうでない場合、BeanCreationException がスローされます。 |
バージョン 5.0 からは、ObjectToMapTransformer
にカスタマイズされた JsonObjectMapper
を提供できます。日付の特別なフォーマットまたは空のコレクション(およびその他の用途)の null が必要な場合。JsonObjectMapper
実装の詳細については、JSONTransformers を参照してください。
ストリームトランス
StreamTransformer
は、InputStream
ペイロードを byte[]
(または charset
が提供されている場合は String
)に変換します。
次の例は、XML で stream-transformer
要素を使用する方法を示しています。
<int:stream-transformer input-channel="directInput" output-channel="output"/> <!-- byte[] -->
<int:stream-transformer id="withCharset" charset="UTF-8"
input-channel="charsetChannel" output-channel="output"/> <!-- String -->
次の例は、StreamTransformer
クラスと @Transformer
アノテーションを使用して、Java でストリームトランスフォーマーを構成する方法を示しています。
@Bean
@Transformer(inputChannel = "stream", outputChannel = "data")
public StreamTransformer streamToBytes() {
return new StreamTransformer(); // transforms to byte[]
}
@Bean
@Transformer(inputChannel = "stream", outputChannel = "data")
public StreamTransformer streamToString() {
return new StreamTransformer("UTF-8"); // transforms to String
}
JSONTransformers
Spring Integration は、Object-to-JSON および JSON-to-Object トランスフォーマーを提供します。次の例のペアは、XML で宣言する方法を示しています。
<int:object-to-json-transformer input-channel="objectMapperInput"/>
<int:json-to-object-transformer input-channel="objectMapperInput"
type="foo.MyDomainObject"/>
デフォルトでは、前述のリストのトランスフォーマーはバニラ JsonObjectMapper
を使用します。クラスパスからの実装に基づいています。次の例に示すように、適切なオプションを使用して、または必要なライブラリ(GSON など)に基づいて、独自のカスタム JsonObjectMapper
実装を提供できます。
<int:json-to-object-transformer input-channel="objectMapperInput"
type="something.MyDomainObject" object-mapper="customObjectMapper"/>
バージョン 3.0 以降、 |
FactoryBean
またはファクトリメソッドを使用して、必要な特性を持つ JsonObjectMapper
を作成することを検討できます。次の例は、そのようなファクトリの使用方法を示しています。
public class ObjectMapperFactory {
public static Jackson2JsonObjectMapper getMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
return new Jackson2JsonObjectMapper(mapper);
}
}
次の例は、XML で同じことを行う方法を示しています
<bean id="customObjectMapper" class="something.ObjectMapperFactory"
factory-method="getMapper"/>
バージョン 2.2 以降、
|
バージョン 3.0 から、ObjectToJsonTransformer
は、ソース型を反映するヘッダーをメッセージに追加します。同様に、JsonToObjectTransformer
は JSON をオブジェクトに変換するときにこれらの型ヘッダーを使用できます。これらのヘッダーは AMQP アダプターにマップされるため、Spring-AMQP JsonMessageConverter
(Javadoc) と完全に互換性があります。
これにより、次のフローが特別な構成なしで機能します。
…→amqp-outbound-adapter---→
---→amqp-inbound-adapter→json-to-object-transformer→…
発信アダプターが
JsonMessageConverter
で構成され、受信アダプターがデフォルトのSimpleMessageConverter
を使用する場合。…→object-to-json-transformer→amqp-outbound-adapter---→
---→amqp-inbound-adapter→…
発信アダプターが
SimpleMessageConverter
で構成され、受信アダプターがデフォルトのJsonMessageConverter
を使用する場合。…→object-to-json-transformer→amqp-outbound-adapter---→
---→amqp-inbound-adapter→json-to-object-transformer→
両方のアダプターが
SimpleMessageConverter
で構成されている場合。
ヘッダーを使用して型を判別する場合、class 属性はヘッダーより優先されるため、class 属性を指定しないでください。 |
JSONTransformers に加えて、Spring Integration は式で使用するための組み込み #jsonPath
SpEL 関数を提供します。詳細については、Spring 式言語 (SpEL) を参照してください。
バージョン 3.0 以降、Spring Integration は式で使用するための組み込み #xpath
SpEL 関数も提供します。詳細については、# xpath SpEL 関数を参照してください。
バージョン 4.0 以降、ObjectToJsonTransformer
は resultType
プロパティをサポートし、ノード JSON 表現を指定します。結果のノードツリー表現は、提供された JsonObjectMapper
の実装に依存します。デフォルトでは、ObjectToJsonTransformer
は Jackson2JsonObjectMapper
を使用し、オブジェクトの変換をノードツリーに ObjectMapper#valueToTree
メソッドに委譲します。ノード JSON 表現は、ダウンストリームメッセージフローが JSON データのプロパティにアクセスする SpEL 式を使用する場合、JsonPropertyAccessor
を使用する効率を提供します。詳細については、プロパティアクセサーを参照してください。
バージョン 5.1 から、resultType
を BYTES
として構成して、このデータ型で動作するダウンストリームハンドラーで作業する場合に便利な byte[]
ペイロードを持つメッセージを生成できます。
バージョン 5.2 以降では、JsonToObjectTransformer
を ResolvableType
で構成して、ターゲット JSON プロセッサーでの逆直列化中にジェネリクスをサポートできます。また、このコンポーネントは、最初に JsonHeaders.RESOLVABLE_TYPE
または JsonHeaders.TYPE_ID
の存在についてリクエストメッセージヘッダーを参照し、それ以外の場合は構成された型にフォールバックするようになりました。ObjectToJsonTransformer
は、考えられるダウンストリームシナリオのリクエストメッセージペイロードに基づいて JsonHeaders.RESOLVABLE_TYPE
ヘッダーも設定するようになりました。
バージョン 5.2.6 以降では、JsonToObjectTransformer
に valueTypeExpression
を指定して、ペイロードの ResolvableType
を解決し、リクエストメッセージに対して実行時に JSON から変換することができます。デフォルトでは、リクエストメッセージで JsonHeaders
を調べます。この式が null
または ResolvableType
を返す場合、建物が ClassNotFoundException
をスローすると、トランスフォーマーは提供された targetType
にフォールバックします。JsonHeaders
は実際のクラス値を持たない可能性があるため、このロジックは式として存在しますが、一部の外部レジストリに従ってターゲットクラスにマップする必要がある型 ID を持っている可能性があります。
Apache AvroTransformers
バージョン 5.2 には、Apache Avro との間で変換するためのシンプルなトランスフォーマーが追加されました。
スキーマレジストリがないという点で、洗練されていません。トランスフォーマーは、Avro スキーマから生成された SpecificRecord
実装に組み込まれたスキーマを使用するだけです。
SimpleToAvroTransformer
に送信されるメッセージには、SpecificRecord
を実装するペイロードが必要です。トランスフォーマーは複数の型を処理できます。SimpleFromAvroTransformer
は、デシリアライズするデフォルトの型として使用される SpecificRecord
クラスで構成する必要があります。setTypeExpression
メソッドを使用して、逆直列化する型を決定する SpEL 式を指定することもできます。デフォルトの SpEL 式は headers[avro_type]
(AvroHeaders.TYPE
)であり、デフォルトでは、SimpleToAvroTransformer
によってソースクラスの完全修飾クラス名が設定されます。式が null
を返す場合、defaultType
が使用されます。
SimpleToAvroTransformer
には setTypeExpression
メソッドもあります。これにより、プロデューサーとコンシューマーの分離が可能になり、送信者はヘッダーを型を表すトークンに設定し、コンシューマーはそのトークンを型にマッピングできます。
アノテーション付きの Transformer の構成
@Transformer
アノテーションは、Message
型またはメッセージペイロード型のいずれかを期待するメソッドに追加できます。戻り値は、<transformer>
要素を説明するセクションで前述したのとまったく同じ方法で処理されます。次の例は、@Transformer
アノテーションを使用して String
を Order
に変換する方法を示しています。
@Transformer
Order generateOrder(String productId) {
return new Order(productId);
}
Transformer メソッドは、Annotation Support
でドキュメント化されているように、@Header
および @Headers
アノテーションも受け入れることができます。次の例は、@Header
アノテーションの使用方法を示しています。
@Transformer
Order generateOrder(String productId, @Header("customerName") String customer) {
return new Order(productId, customer);
}
アノテーションを使用したエンドポイントへのアドバイスも参照してください。
ヘッダーフィルター
変換のユースケースは、いくつかのヘッダーを削除するだけの簡単な場合もあります。このような使用例のために、Spring Integration は、出力メッセージから削除する特定のヘッダー名を指定できるヘッダーフィルターを提供します(たとえば、セキュリティ上の理由からヘッダーを削除したり、一時的にのみ必要な値を削除したりします)。基本的に、ヘッダーフィルターはヘッダーエンリッチャーの反対です。後者はヘッダーエンリッチャーで説明されています。次の例では、ヘッダーフィルターを定義しています。
<int:header-filter input-channel="inputChannel"
output-channel="outputChannel" header-names="lastName, state"/>
ご覧のとおり、ヘッダーフィルターの構成は非常に簡単です。これは、入力チャネルと出力チャネル、および header-names
属性を持つ一般的なエンドポイントです。この属性は、削除する必要があるヘッダーの名前 (複数の場合はコンマで区切る) を受け入れます。前の例では、'lastName' および 'state' という名前のヘッダーは送信メッセージに存在しません。
コーデックベースの Transformers
コーデックを参照してください。