編解碼器
Spring Integration 4.2 版引入了 Codec 抽象。編解碼器將物件編碼和解碼為 byte[]。它們提供了 Java 序列化的替代方案。一個優點是,通常情況下,物件無需實現 Serializable。我們提供了一個使用 Kryo 進行序列化的實現,但您可以提供自己的實現以用於以下任何元件中
-
EncodingPayloadTransformer -
DecodingTransformer -
CodecMessageConverter
EncodingPayloadTransformer
此轉換器使用編解碼器將負載編碼為 byte[]。它不影響訊息頭。
有關更多資訊,請參閱Javadoc。
DecodingTransformer
此轉換器使用編解碼器解碼 byte[]。它需要配置一個 Class 以解碼物件(或解析為 Class 的表示式)。如果生成的物件是 Message<?>,則不保留入站訊息頭。
有關更多資訊,請參閱Javadoc。
CodecMessageConverter
某些端點(例如 TCP 和 Redis)沒有訊息頭的概念。它們支援使用 MessageConverter,而 CodecMessageConverter 可用於將訊息轉換為 byte[] 或從 byte[] 轉換訊息以進行傳輸。
有關更多資訊,請參閱Javadoc。
Kryo
目前,這是 Codec 的唯一實現,它提供了三種 Codec
-
PojoCodec:用於轉換器中 -
MessageCodec:用於CodecMessageConverter中 -
CompositeCodec:用於轉換器中
該框架提供了幾個自定義序列化器
-
FileSerializer -
MessageHeadersSerializer -
MutableMessageHeadersSerializer
第一個可以透過使用 FileKryoRegistrar 初始化來與 PojoCodec 一起使用。第二個和第三個與 MessageCodec 一起使用,該 MessageCodec 使用 MessageKryoRegistrar 初始化。
CompositeCodec
CompositeCodec 是一種將多個編解碼器組合成單個編解碼器的編解碼器,將編碼和解碼操作委託給適當的特定型別編解碼器。此實現將物件型別與其相應的編解碼器關聯起來,同時為未註冊的型別提供一個備用的預設編解碼器。
下面是一個示例實現
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 還為每個原始型別以及 String、Collection 和 Map 註冊預設序列化器。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());
}
}
如 Kryo 文件中所述,Serializer 介面公開了 Kryo、Input 和 Output,它們提供了對要包含的欄位和其他內部設定的完全控制。
| 註冊自定義序列化器時,您需要一個註冊 ID。註冊 ID 是任意的。但是,在我們的例子中,ID 必須明確定義,因為分散式應用程式中的每個 Kryo 例項都必須使用相同的 ID。Kryo 建議使用小的正整數並保留一些 ID(值 < 10)。Spring Integration 目前預設使用 40、41 和 42(用於前面提到的檔案和訊息頭序列化器)。我們建議您從 60 開始,以便為框架的擴充套件留出空間。您可以透過配置前面提到的註冊器來覆蓋這些框架預設值。 |
使用自定義 Kryo 序列化器
如果您需要自定義序列化,請參閱 Kryo 文件,因為您需要使用原生 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 註冊類,這可能會使該技術在某些情況下無用。