B.5 Annotation Support

In addition to the XML namespace support for configuring Message Endpoints, it is also possible to use annotations. First, Spring Integration provides the class-level @MessageEndpoint as a stereotype annotation meaning that is itself annotated with Spring's @Component annotation and therefore is recognized automatically as a bean definition when using Spring component-scanning.

Even more importantly are the various Method-level annotations that indicate the annotated method is capable of handling a message. The following example demonstrates both:

@MessageEndpoint
public class FooService {

    @ServiceActivator
    public void processMessage(Message message) {
        ...
    }
}

Exactly what it means for the method to "handle" the Message depends on the particular annotation. The following are available with Spring Integration, and the behavior of each is described in its own chapter or section within this reference: @Transformer, @Router, @Splitter, @Aggregator, @ServiceActivator, and @ChannelAdapter.

[Note]Note
The @MessageEndpoint is not required if using XML configuration in combination with annotations. If you want to configure a POJO reference from the "ref" attribute of a <service-activator/> element, it is sufficient to provide the method-level annotations. In that case, the annotation prevents ambiguity even when no "method" attribute exists on the <service-activator/> element.

In most cases, the annotated handler method should not require the Message type as its parameter. Instead, the method parameter type can match the message's payload type.

public class FooService {

    @Handler
    public void bar(Foo foo) {
        ...
    }

}

When the method parameter should be mapped from a value in the MessageHeaders, another option is to use the parameter-level @Header annotation. In general, methods annotated with the Spring Integration annotations can either accept the Message itself, the message payload, or a header value (with @Header) as the parameter. In fact, the method can accept a combination, such as:

public class FooService {

    @ServiceActivator
    public void bar(String payload, @Header("x") int valueX, @Header("y") int valueY) {
        ...
    }

}

There is also a @Headers annotation that provides all of the Message headers as a Map:

public class FooService {

    @ServiceActivator
    public void bar(String payload, @Headers Map<String, Object> headerMap) {
        ...
    }

}

[Tip]Tip
A Map-typed argument does not strictly require the use of the @Headers annotation. In other words the following is also valid:
public void bar(String payload, Map<String, Object> headerMap)
However this can lead to unresolvable ambiguities if the payload is itself a Map. For that reason, we highly recommend using the annotation whenever expecting the headers. For a much more detailed description, see the javadoc for MethodParameterMessageMapper.

For several of these annotations, when a Message-handling method returns a non-null value, the endpoint will attempt to send a reply. This is consistent across both configuration options (namespace and annotations) in that such an endpoint's output channel will be used if available, and the REPLY_CHANNEL message header value will be used as a fallback.

[Tip]Tip
The combination of output channels on endpoints and the reply channel message header enables a pipeline approach where multiple components have an output channel, and the final component simply allows the reply message to be forwarded to the reply channel as specified in the original request message. In other words, the final component depends on the information provided by the original sender and can dynamically support any number of clients as a result. This is an example of Return Address.

In addition to the examples shown here, these annotations also support inputChannel and outputChannel properties.

public class FooService {

    @ServiceActivator(inputChannel="input", outputChannel="output")
    public void bar(String payload, @Headers Map<String, Object> headerMap) {
        ...
    }

}

That provides a pure annotation-driven alternative to the XML configuration. However, it is generally recommended to use XML for the endpoints, since it is easier to keep track of the overall configuration in a single, external location (and besides the namespace-based XML configuration is not very verbose). If you do prefer to provide channels with the annotations however, you just need to enable a BeanPostProcessor. The following element should be added:

 <annotation-config/> 

[Note]Note
When configuring the "inputChannel" and "outputChannel" with annotations, the "inputChannel" must be a reference to a SubscribableChannel instance. Otherwise, it would be necessary to also provide the full poller configuration via annotations, and those settings (e.g. the trigger for scheduling the poller) should be externalized rather than hard-coded within an annotation. If the input channel that you want to receive Messages from is indeed a PollableChannel instance, one option to consider is the Messaging Bridge. Spring Integration's "bridge" element can be used to connect a PollableChannel directly to a SubscribableChannel. Then, the polling metadata is externally configured, but the annotation option is still available. For more detail see Chapter 15, Messaging Bridge.