Codec
Version 4.2 of Spring Integration introduced the Codec
abstraction.
Codecs encode and decode objects to and from byte[]
.
They offer an alternative to Java serialization.
One advantage is that, typically, objects need not implement Serializable
.
We provide one implementation that uses Kryo for serialization, but you can provide your own implementation for use in any of the following components:
-
EncodingPayloadTransformer
-
DecodingTransformer
-
CodecMessageConverter
EncodingPayloadTransformer
This transformer encodes the payload to a byte[]
by using the codec.
It does not affect message headers.
See the Javadoc for more information.
DecodingTransformer
This transformer decodes a byte[]
by using the codec.
It needs to be configured with the Class
to which the object should be decoded (or an expression that resolves to a Class
).
If the resulting object is a Message<?>
, inbound headers are not retained.
See the Javadoc for more information.
CodecMessageConverter
Certain endpoints (such as TCP and Redis) have no concept of message headers.
They support the use of a MessageConverter
, and the CodecMessageConverter
can be used to convert a message to or from a byte[]
for transmission.
See the Javadoc for more information.
Kryo
Currently, this is the only implementation of Codec
, and it provides two kinds of Codec
:
-
PojoCodec
: Used in the transformers -
MessageCodec
: Used in theCodecMessageConverter
The framework provides several custom serializers:
-
FileSerializer
-
MessageHeadersSerializer
-
MutableMessageHeadersSerializer
The first can be used with the PojoCodec
by initializing it with the FileKryoRegistrar
.
The second and third are used with the MessageCodec
, which is initialized with the MessageKryoRegistrar
.
Customizing Kryo
By default, Kryo delegates unknown Java types to its FieldSerializer
.
Kryo also registers default serializers for each primitive type, along with String
, Collection
, and Map
.
FieldSerializer
uses reflection to navigate the object graph.
A more efficient approach is to implement a custom serializer that is aware of the object’s structure and can directly serialize selected primitive fields.
The following example shows such a serializer:
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());
}
}
The Serializer
interface exposes Kryo
, Input
, and Output
, which provide complete control over which fields are included and other internal settings, as described in the Kryo documentation.
When registering your custom serializer, you need a registration ID. The registration IDs are arbitrary. However, in our case, the IDs must be explicitly defined, because each Kryo instance across the distributed application must use the same IDs. Kryo recommends small positive integers and reserves a few ids (value < 10). Spring Integration currently defaults to using 40, 41, and 42 (for the file and message header serializers mentioned earlier). We recommend you start at 60, to allow for expansion in the framework. You can override these framework defaults by configuring the registrars mentioned earlier. |
Using a Custom Kryo Serializer
If you need custom serialization, see the Kryo documentation, because you need to use the native API to do the customization.
For an example, see the MessageCodec
implementation.
Implementing KryoSerializable
If you have write access to the domain object source code, you can implement KryoSerializable
as described here.
In this case, the class provides the serialization methods itself and no further configuration is required.
However benchmarks have shown this is not quite as efficient as registering a custom serializer explicitly.
The following example shows a custom Kryo serializer:
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();
}
}
You can also use this technique to wrap a serialization library other than Kryo.
Using the @DefaultSerializer
Annotation
Kryo also provides a @DefaultSerializer
annotation, as described here.
@DefaultSerializer(SomeClassSerializer.class)
public class SomeClass {
// ...
}
If you have write access to the domain object, this may be a simpler way to specify a custom serializer. Note that this does not register the class with an ID, which may make the technique unhelpful for certain situations.