4.3 Annotations

In addition to the XML namespace support for configuring Message Endpoints, it is also possible to use annotations. The class-level @MessageEndpoint annotation indicates that the annotated class is capable of being registered as an endpoint, and the method-level @Handler annotation indicates that the annotated method is capable of handling a message.

@MessageEndpoint(input="fooChannel")
public class FooService {

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

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.

@MessageEndpoint(input="fooChannel")
public class FooService {

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

When the method parameter should be mapped from a value in the MessageHeader, another option is to use the @HeaderAttribute and/or @HeaderProperty parameter annotations.

@MessageEndpoint(input="fooChannel")
public class FooService {

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

@MessageEndpoint(input="fooChannel")
public class FooService {

    @Handler
    public void bar(@HeaderProperty("foo") String input) {
        ...
    }
}

As described in the previous section, when the handler 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 the the endpoint's output channel will be used if available, and the message header's 'returnAddress' value will be the fallback. To configure the output channel for an annotation-driven endpoint, provide the 'output' attribute on the @MessageEndpoint.

@MessageEndpoint(input="exampleChannel", output="replyChannel")

Just as the 'schedule' sub-element and its 'period' attribute can be provided for a namespace-based endpoint, the @Polled annotation can be provided with the @MessageEndpoint annotation.

@MessageEndpoint(input="exampleChannel")
@Polled(period=3000)
public class FooService {
    ...
}

Likewise, @Concurrency provides an annotation-based equivalent of the <concurrency/> element:

@MessageEndpoint(input="fooChannel")
@Concurrency(coreSize=5, maxSize=20)
public class FooService {

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

Two additional annotations are supported, and both act as a special form of handler method: @Router and @Splitter. As with the @Handler annotation, methods annotated with either of these two annotations can either accept the Message itself or the message payload type as the parameter. When using the @Router annotation, the annotated method can return either the MessageChannel or String type. In the case of the latter, the endpoint will resolve the channel name as it does for the default output. Additionally, the method can return either a single value or a collection. When a collection is returned, the reply message will be sent to multiple channels. To summarize, the following method signatures are all valid.

@Router
public MessageChannel route(Message message) {...}

@Router
public List<MessageChannel> route(Message message) {...}

@Router
public String route(Foo payload) {...}

@Router
public List<String> route(Foo payload) {...}

In addition to payload-based routing, a common requirement is to route based on metadata available within the message header as either a property or attribute. Rather than requiring use of the Message type as the method parameter, the @Router annotation may also use the same parameter annotations that were introduced above.

@Router
public String route(@HeaderProperty("customerType") String customerType)

@Router
public List<String> route(@HeaderAttribute("orderStatus") OrderStatus status)

The @Splitter annotation is also applicable to methods that expect either the Message type or the message payload type, and the return values of the method should be a collection of any type. If the returned values are not actual Message objects, then each of them will be sent as the payload of a message. Those messages will be sent to the output channel as designated for the endpoint on which the @Splitter is defined.

@Splitter
List<LineItem> extractItems(Order order) {
    return order.getItems()
}

The @Publisher annotation is convenient for sending messages with AOP after-returning advice. For example, each time the following method is invoked, its return value will be sent to the "fooChannel":

@Publisher(channel="fooChannel")
public String foo() {
    return "bar";
}

Similarly, the @Subscriber annotation triggers the retrieval of messages from a channel, and the payload of each message will then be sent as input to an arbitrary method. This is one of the simplest ways to configure asynchronous, event-driven behavior:

@Subscriber(channel="fooChannel")
public void log(String foo) {
    System.out.println(foo);
}