To allow you to propagate information about the content type of produced messages, Spring Cloud Stream attaches, by default, a contentType header to outbound messages. For middleware that does not directly support headers, Spring Cloud Stream provides its own mechanism of automatically wrapping outbound messages in an envelope of its own. For middleware that does support headers, Spring Cloud Stream applications may receive messages with a given content type from non-Spring Cloud Stream applications.

The content type resolution process have been redesigned for Spring Cloud Stream 2.0.

Please read the migrating from 1.3 section to understand the changes when interacting with applications using versions of the framework.

The framework depends on a contentType to be present as a header in order to know how serialize/deserialize a payload.

Spring Cloud Stream allows you to declaratively configure type conversion for inputs and outputs using the spring.cloud.stream.bindings.<channelName>.content-type property of a binding. Note that general type conversion may also be accomplished easily by using a transformer inside your application.

[Note] Note

For both input and output channel, setting a contentType via a property or via annotation only triggers the default converter if a message header with value contentType is not present. This is useful for cases where you just want to send a POJO without sending any header information, or to consume messages that do not have a contentType header present. The framework will always override any default settings with the value found on the message headers.

[Tip] Tip

Although contentType became a required property, the framework will set a default value of application/json for all input/output channels if one is not provided by the user.

Starting with version 2.0, the framework will no longer try to infer a contentType based on the payload T of a Message<T> . It will instead use the contentType header (or the default provided by the framework) to configure the right MessageConverter to serialize the payload into byte[] .

The contentType you set is a hint to activate the corresponding MessageConverter . The converter can then modify the contentType to augment the information, such as the case with Kryo and Avro conveters.

For outbound messages, if your payload is of typ byte[] , the framework will skip the conversion logic, and just write those bytes to the wire. In this case, if contentType of the message is absent, it will set the default value specified to channel.

[Tip] Tip

If you intend to bypass conversion, just make sure you set the appropriate contentType header, otherwise you could be sending some arbitrary binary data, and the framework may set the header as application/json (default).

The following snippet shows how you can bypass conversion and set the correct contentType header.

@Autowired
private Source source;
public void sendImageData(File f) throws Exception {
    byte[] data = Files.readAllBytes(f.toPath());
    MimeType mimeType = (f.getName().endsWith("gif")) ? MimeTypeUtils.IMAGE_GIF : MimeTypeUtils.IMAGE_JPEG;
    source.output().send(MessageBuilder.withPayload(data)
            .setHeader(MessageHeaders.CONTENT_TYPE, mimeType)
            .build());
}

Regardless of contentType used, the result is always a Message<byte[]> with a header contentType set. This is what gets passed to the binder to be sent over the wire.

content-type header MessageConverter content-type augmented Supported types Comments

application/json

CustomMappingJackson2MessageConverter

application/json

POJO, primitives and Strings that represent JSON data

It’s the default converter if none is specified. Note that if you send a raw String it will be quoted

text/plain

ObjectStringMessageConverter

text/plain

Invokes toString() of the object

application/x-spring-tuple

TupleJsonMessageConverter

application/x-spring-tuple

org.springframework.tuple.Tuple

application/x-java-serialized-object

JavaSerializationMessageConverter

application/x-java-serialized-object

Any Java type that implements Serializable

This converter uses java native serialization. Receivers of this data must have the same class on the classpath.

application/x-java-object

KryoMessageConverter

application/x-java-object;type=<Class being serialized>

Any Java type that can be serialized using Kryo

Receivers of this data must have the same class on the classpath.

application/avro

AvroMessageConverter

application/avro

A Generic or SpecificRecord from Avro types, a POJO if reflection is used

Avro needs an associated schema to write/read data. Please refer to the section on the docs on how to use it properly

For input channels, Spring Cloud Stream uses @StreamListener and @ServiceActivator content handling to support the conversion. It does so by checking either the channel content-type set via @Input(contentType="text/plain") annotation or via spring.cloud.stream.bindings.<channel>.contentType property, or the presense of a header contentType .

The framework will check the contentType set for the Message, select the appropriate MessageConverter and apply conversion passing the argument as the target type.

If the converter does not support the target type it will return null , if all configured converters return null , a MessageConversionException is thrown.

Just like output channels, if your method payload argument is of type Message<byte[]> , byte[] or Message<?> conversion is skipped and you get the raw bytes from the wire, plus the corresponding headers.

[Tip] Tip

Remember, the MessageHeader always takes precedence over the annotation or property configuration.

content-type header MessageConverter Supported target type Comments

application/json

CustomMappingJackson2MessageConverter

POJO or String

text/plain

ObjectStringMessageConverter

String

application/x-spring-tuple

TupleJsonMessageConverter

org.springframework.tuple.Tuple

application/x-java-serialized-object

JavaSerializationMessageConverter

Any Java type that implements Serializable

application/x-java-object

KryoMessageConverter

Any Java type that can be serialized using Kryo

application/avro

AvroMessageConverter

A Generic or SpecificRecord from Avro types, a POJO if reflection is used

Avro needs an associated schema to write/read data. Please refer to the section on the docs on how to use it properly

Besides the conversions that it supports out of the box, Spring Cloud Stream also supports registering your own message conversion implementations. This allows you to send and receive data in a variety of custom formats, including binary, and associate them with specific contentTypes .

Spring Cloud Stream registers all the beans of type org.springframework.messaging.converter.MessageConverter that are qualifeied using @StreamConverter annotation, as custom message converters along with the out of the box message converters.

[Note] Note

The framework requires the @StreamConverter qualifier annotation to avoid picking up other converters that may be present on the ApplicationContext and could overlap with the default ones.

If your message converter needs to work with a specific content-type and target class (for both input and output), then the message converter needs to extend org.springframework.messaging.converter.AbstractMessageConverter . For conversion when using @StreamListener , a message converter that implements org.springframework.messaging.converter.MessageConverter would suffice.

Here is an example of creating a message converter bean (with the content-type application/bar ) inside a Spring Cloud Stream application:

@EnableBinding(Sink.class)
@SpringBootApplication
public static class SinkApplication {
    @Bean
    @StreamConverter
    public MessageConverter customMessageConverter() {
        return new MyCustomMessageConverter();
}
public class MyCustomMessageConverter extends AbstractMessageConverter {
    public MyCustomMessageConverter() {
        super(new MimeType("application", "bar"));
    @Override
    protected boolean supports(Class<?> clazz) {
        return (Bar.class.equals(clazz));
    @Override
    protected Object convertFromInternal(Message<?> message, Class<?> targetClass, Object conversionHint) {
        Object payload = message.getPayload();
        return (payload instanceof Bar ? payload : new Bar((byte[]) payload));
}

Spring Cloud Stream also provides support for Avro-based converters and schema evolution. See the specific section for details.