编解码器

Spring Integration 4.2 版本引入了 Codec 抽象。 编解码器将对象编码和解码为 byte[]。 它们提供了 Java 序列化的替代方案。 一个优点是,通常对象无需实现 Serializable。 我们提供了一个使用 Kryo 进行序列化的实现,但您可以提供自己的实现在以下任何组件中使用:spring-doc.cadn.net.cn

EncodingPayloadTransformer

此转换器使用编解码器将有效负载编码为 byte[]。 它不影响消息头。spring-doc.cadn.net.cn

有关更多信息,请参阅javadocspring-doc.cadn.net.cn

DecodingTransformer

此转换器使用编解码器对 byte[] 进行解码。 它需要配置目标 Class,对象将被解码到该位置(或解析为 Class 的表达式)。 如果生成的对象是 Message<?>,则不会保留入站标头。spring-doc.cadn.net.cn

有关更多信息,请参阅javadocspring-doc.cadn.net.cn

CodecMessageConverter

某些端点(如 TCP 和 Redis)没有消息头的概念。 它们支持使用 MessageConverter,并且可以使用 CodecMessageConverter 将消息转换为 byte[] 或从 byte[] 转换以进行传输。spring-doc.cadn.net.cn

有关更多信息,请参阅javadocspring-doc.cadn.net.cn

Kryo

目前,这是 Codec 的唯一实现,它提供三种类型的 Codecspring-doc.cadn.net.cn

该框架提供多种自定义序列化器:spring-doc.cadn.net.cn

第一个可以与 PojoCodec 一起使用,通过将其用 FileKryoRegistrar 初始化。 第二个和第三个与 MessageCodec 一起使用,该对象由 MessageKryoRegistrar 初始化。spring-doc.cadn.net.cn

复合编解码器

CompositeCodec 是一个编解码器,它将多个编解码器组合为一个单一的编解码器,并将编码和解码操作委托给适当的类型特定编解码器。 该实现将对象类型与其相应的编解码器关联起来,同时为未注册的类型提供回退默认编解码器。spring-doc.cadn.net.cn

以下是一个示例实现:spring-doc.cadn.net.cn

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){}

在某些情况下,单个类型的对象可能会返回多个编解码器。 在这种情况下,将抛出 IllegalStateExceptionspring-doc.cadn.net.cn

该类使用 ClassUtils.findClosestMatch 为给定对象类型选择合适的编解码器。 当多个编解码器匹配某个对象类型时,ClassUtils.findClosestMatch 提供 failOnTie 选项。 如果 failOnTiefalse,它将返回任意一个匹配的编解码器。 如果 failOnTietrue 且存在多个匹配的编解码器,则会抛出 IllegalStateExceptionCompositeCodecfailOnTie 设置为 true,因此如果多个编解码器匹配,将抛出 IllegalStateException

自定义 Kryo

默认情况下,Kryo 将未知的 Java 类型委托给其 FieldSerializer。 Kryo 还为每种基本类型注册了默认序列化器,以及 StringCollectionMapFieldSerializer 使用反射来导航对象图。 一种更高效的方法是实现一个自定义序列化器,该序列化器能够感知对象的结构,并可以直接序列化选定的基本字段。 以下示例展示了此类序列化器:spring-doc.cadn.net.cn

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 文档中所述。spring-doc.cadn.net.cn

在注册自定义序列化器时,您需要一个注册 ID。 注册 ID 可以是任意的。 然而,在我们的场景中,必须显式定义这些 ID,因为分布式应用中的每个 Kryo 实例都必须使用相同的 ID。 Kryo 建议使用小的正整数,并保留较小的 ID(值 < 10)。 Spring Integration 当前默认使用 40、41 和 42(用于前面提到的文件和消息头序列化器)。 我们建议您从 60 开始,以便为框架的扩展留出空间。 您可以通过配置前面提到的注册器来覆盖这些框架默认值。

使用自定义 Kryo 序列化器

如果您需要自定义序列化,请参阅 Kryo 文档,因为您需要使用原生 API 来完成自定义。 有关示例,请参阅 org.springframework.integration.codec.kryo.MessageCodec 实现。spring-doc.cadn.net.cn

实现 KryoSerializable

如果您无法访问领域对象的源代码(write),您可以按照此处所述实现KryoSerializable。 在这种情况下,该类自行提供序列化方法,无需进一步配置。 然而,基准测试表明,这种方式不如显式注册自定义序列化器高效。 以下示例展示了一个自定义 Kryo 序列化器:spring-doc.cadn.net.cn

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 之外的其他序列化库。spring-doc.cadn.net.cn

使用@DefaultSerializer注解

Kryo 还提供 @DefaultSerializer 注解,如此处所述。spring-doc.cadn.net.cn

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

如果您无法访问域对象(write),这可能是一种指定自定义序列化器的更简单方法。 请注意,这不会将类注册为 ID,因此该技术在某些情况下可能没有帮助。spring-doc.cadn.net.cn