最新の安定バージョンについては、Spring Integration 7.1.0 を使用してください!

コーデック

Spring Integration のバージョン 4.2 は、Codec 抽象化を導入しました。コーデックは、byte[] との間でオブジェクトをエンコードおよびデコードします。Java 直列化の代替手段を提供します。1 つの利点は、通常、オブジェクトが Serializable を実装する必要がないことです。Kryo [GitHub] (英語) を使用して直列化する実装を 1 つ提供していますが、次のコンポーネントで使用する独自の実装を提供できます。

  • EncodingPayloadTransformer

  • DecodingTransformer

  • CodecMessageConverter

EncodingPayloadTransformer

このトランスフォーマーは、コーデックを使用してペイロードを byte[] にエンコードします。メッセージヘッダーには影響しません。

詳細については、Javadoc を参照してください。

DecodingTransformer

このトランスフォーマーは、コーデックを使用して byte[] をデコードします。オブジェクトのデコード先の Class (または Class に解決される式)で構成する必要があります。結果のオブジェクトが Message<?> の場合、受信ヘッダーは保持されません。

詳細については、Javadoc を参照してください。

CodecMessageConverter

特定のエンドポイント(TCP や Redis など)には、メッセージヘッダーの概念がありません。それらは MessageConverter の使用をサポートし、CodecMessageConverter は、送信のためにメッセージを byte[] との間で変換するために使用できます。

詳細については、Javadoc を参照してください。

Kryo

現在、これが Codec の唯一の実装であり、3 種類の Codec を提供します。

  • PojoCodec: トランスで使用

  • MessageCodecCodecMessageConverter で使用

  • CompositeCodec: トランスフォーマーに使用

フレームワークは、いくつかのカスタムシリアライザーを提供します。

  • FileSerializer

  • MessageHeadersSerializer

  • MutableMessageHeadersSerializer

最初のものは、FileKryoRegistrar で初期化することにより、PojoCodec で使用できます。2 番目と 3 番目は MessageCodec で使用され、MessageKryoRegistrar で初期化されます。

CompositeCodec

CompositeCodec は、複数のコーデックを 1 つのコーデックに統合し、エンコードとデコードの処理を適切な型固有のコーデックに委譲するコーデックです。この実装は、オブジェクト型を適切なコーデックに関連付けるとともに、未登録の型に対してはフォールバックとなるデフォルトのコーデックを提供します。

実装例を以下に示します。

void encodeDecodeSample() {
        Codec codec = getFullyQualifiedCodec();

        //Encode and Decode a Dog Object
        Dog dog = new Dog("Wolfy", 3, "woofwoof");
        dog = codec.decode(
                codec.encode(dog),
                Dog.class);
        System.out.println(dog);

        //Encode and Decode a Cat Object
        Cat cat = new Cat("Kitty", 2, 8);
        cat = codec.decode(
                codec.encode(cat),
                Cat.class);
        System.out.println(cat);

        //Use the default code if the type being decoded and encoded is not Cat or dog.
        Animal animal = new Animal("Badger", 5);
        Animal animalOut = codec.decode(
                codec.encode(animal),
                Animal.class);
        System.out.println(animalOut);
}

/**
 * Create and return a {@link CompositeCodec} that associates {@code Dog} and {@code Cat}
 * classes with their respective {@link PojoCodec} instances, while providing a default
 * codec for {@code Animal} types.
 * <p>
 * @return a fully qualified {@link CompositeCodec} for {@code Dog}, {@code Cat},
 *     and fallback for {@code Animal}
 */
static Codec getFullyQualifiedCodec() {
    Map<Class<?>, Codec> codecs = new HashMap<Class<?>, Codec>();
    codecs.put(Dog.class, new PojoCodec(new KryoClassListRegistrar(Dog.class)));
    codecs.put(Cat.class, new PojoCodec(new KryoClassListRegistrar(Cat.class)));
    return new CompositeCodec(codecs, new PojoCodec(
            new KryoClassListRegistrar(Animal.class)));
}

// Records that will be encoded and decoded in this sample
record Dog(String name, int age, String tag) {}
record Cat(String name, int age, int lives) {}
record Animal(String name, int age){}

場合によっては、単一のオブジェクト型が複数のコーデックを返すことがあります。その場合、IllegalStateException がスローされます。

このクラスは、ClassUtils.findClosestMatch を使用して、指定されたオブジェクト型に適したコーデックを選択します。複数のコーデックがオブジェクト型に一致する場合、ClassUtils.findClosestMatch は failOnTie オプションを提供します。failOnTie が false の場合、一致するコーデックのいずれかを返します。failOnTie が true で、複数のコーデックが一致する場合、IllegalStateException をスローします。CompositeCodec は failOnTie を true に設定するため、複数のコーデックが一致する場合は IllegalStateException がスローされます。

Kryo のカスタマイズ

デフォルトでは、Kryo は未知の Java 型を FieldSerializer に委譲します。Kryo は、StringCollectionMap とともに、各プリミティブ型のデフォルトのシリアライザーも登録します。FieldSerializer は、反射を使用してオブジェクトグラフをナビゲートします。より効率的なアプローチは、オブジェクトの構造を認識し、選択したプリミティブフィールドを直接直列化できるカスタムシリアライザーを実装することです。次の例は、そのようなシリアライザーを示しています。

public class AddressSerializer extends Serializer<Address> {

    @Override
    public void write(Kryo kryo, Output output, Address address) {
        output.writeString(address.getStreet());
        output.writeString(address.getCity());
        output.writeString(address.getCountry());
    }

    @Override
    public Address read(Kryo kryo, Input input, Class<Address> type) {
        return new Address(input.readString(), input.readString(), input.readString());
    }
}

Serializer インターフェースは KryoInputOutput を公開します。これにより、Kryo ドキュメント [GitHub] (英語) に従って、含まれるフィールドやその他の内部設定を完全に制御できます。

カスタムシリアライザーを登録する際には、登録 ID が必要です。登録 ID は任意です。ただし、分散アプリケーション全体の各 Kryo インスタンスは同じ ID を使用する必要があるため、このケースでは ID を明示的に定義する必要があります。Kryo は小さな正の整数を推奨し、小さな ID(10 未満の値)を予約しています。Spring Integration は現在、デフォルトで 40, 41,, 42 を使用しています(前述のファイルおよびメッセージヘッダーシリアライザー用)。フレームワークの拡張性を考慮して、60 から始めることをお勧めします。前述のレジストラを設定することで、これらのフレームワークのデフォルトをオーバーライドできます。

カスタム Kryo シリアライザーの使用

カスタム直列化が必要な場合は、Kryo [GitHub] (英語) のドキュメントを参照してください。カスタマイズにはネイティブ API を使用する必要があるためです。例については、org.springframework.integration.codec.kryo.MessageCodec の実装を参照してください。

KryoSerializable の実装

ドメインオブジェクトのソースコードへの write アクセス権がある場合は、ここで説明されているように KryoSerializable を実装できます。この場合、クラスは直列化メソッド自体を提供し、それ以上の構成は必要ありません。ただし、ベンチマークは、これがカスタムシリアライザーを明示的に登録するほど効率的ではないことを示しています。次の例は、カスタム Kryo シリアライザーを示しています。

public class Address implements KryoSerializable {

    @Override
    public void write(Kryo kryo, Output output) {
        output.writeString(this.street);
        output.writeString(this.city);
        output.writeString(this.country);
    }

    @Override
    public void read(Kryo kryo, Input input) {
        this.street = input.readString();
        this.city = input.readString();
        this.country = input.readString();
    }
}

この手法を使用して、Kryo 以外の直列化ライブラリをラップすることもできます。

@DefaultSerializer アノテーションの使用

Kryo は、ここで説明するように、@DefaultSerializer アノテーションも提供します。

@DefaultSerializer(SomeClassSerializer.class)
public class SomeClass {
       // ...
}

ドメインオブジェクトへの write アクセス権を持っている場合、これはカスタムシリアライザーを指定するより簡単なメソッドかもしれません。これはクラスに ID を登録しないことに注意してください。