Browse Source

Update docs on @SendTo and @SendToUser

1. Explain that both can be used on the same method
2. Better describe semantics for class vs method level
3. General improvements

Issue: SPR-16336
pull/1856/head
Rossen Stoyanchev 8 years ago
parent
commit
d196cdc5cd
  1. 5
      spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/SendTo.java
  2. 16
      spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/SendToUser.java
  3. 10
      spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java
  4. 88
      src/docs/asciidoc/web/websocket.adoc

5
spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/SendTo.java

@ -32,9 +32,8 @@ import org.springframework.messaging.Message;
* convey the destination to use for the reply. In that case, that destination * convey the destination to use for the reply. In that case, that destination
* should take precedence. * should take precedence.
* *
* <p>The annotation may also be placed at class-level if the provider supports * <p>This annotation may be placed class-level in which case it is inherited by
* it to indicate that all related methods should use this destination if none * methods of the class.
* is specified otherwise.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @author Stephane Nicoll * @author Stephane Nicoll

16
spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/SendToUser.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2016 the original author or authors. * Copyright 2002-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -25,13 +25,17 @@ import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor; import org.springframework.core.annotation.AliasFor;
/** /**
* Annotation that indicates that the return value of a message-handling method * Indicates the return value of a message-handling method should be sent as a
* should be sent as a {@link org.springframework.messaging.Message} to the specified * {@link org.springframework.messaging.Message} to the specified destination(s)
* destination(s) prepended with <code>"/user/{username}"</code> where the user name * further prepended with <code>"/user/{username}"</code> where the user name
* is extracted from the headers of the input message being handled. * is extracted from the headers of the input message being handled.
* *
* <p>The annotation may also be placed at class-level in which case all methods * <p>Both {@code @SendTo} and {@code @SendToUser} may be used on the same method
* in the class where the annotation applies will inherit it. * in which case a message is sent to the destinations of both annotations.
*
* <p>This annotation may be placed class-level in which case it is inherited
* by methods of the class. At the same time, method-level {@code @SendTo} or
* {@code @SendToUser} annotations override any such at the class level.
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @author Sam Brannen * @author Sam Brannen

10
spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java

@ -330,15 +330,14 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(); List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();
// Single-purpose return value types // Single-purpose return value types
handlers.add(new ListenableFutureReturnValueHandler()); handlers.add(new ListenableFutureReturnValueHandler());
handlers.add(new CompletableFutureReturnValueHandler()); handlers.add(new CompletableFutureReturnValueHandler());
// Annotation-based return value types // Annotation-based return value types
SendToMethodReturnValueHandler sendToHandler =
new SendToMethodReturnValueHandler(this.brokerTemplate, true); SendToMethodReturnValueHandler sendToHandler = new SendToMethodReturnValueHandler(this.brokerTemplate, true);
if (this.headerInitializer != null) { sendToHandler.setHeaderInitializer(this.headerInitializer);
sendToHandler.setHeaderInitializer(this.headerInitializer);
}
handlers.add(sendToHandler); handlers.add(sendToHandler);
SubscriptionMethodReturnValueHandler subscriptionHandler = SubscriptionMethodReturnValueHandler subscriptionHandler =
@ -350,6 +349,7 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
handlers.addAll(getCustomReturnValueHandlers()); handlers.addAll(getCustomReturnValueHandlers());
// catch-all // catch-all
sendToHandler = new SendToMethodReturnValueHandler(this.brokerTemplate, false); sendToHandler = new SendToMethodReturnValueHandler(this.brokerTemplate, false);
sendToHandler.setHeaderInitializer(this.headerInitializer); sendToHandler.setHeaderInitializer(this.headerInitializer);
handlers.add(sendToHandler); handlers.add(sendToHandler);

88
src/docs/asciidoc/web/websocket.adoc

@ -1214,22 +1214,17 @@ methods as described next.
[[websocket-stomp-message-mapping]] [[websocket-stomp-message-mapping]]
==== `@MessageMapping` ==== `@MessageMapping`
The `@MessageMapping` annotation can be used on methods to route messages based on their `@MessageMapping` can be used to annotate methods to route messages based on their
destination. It is supported at the method level as well as at the type level. At type destination. It is supported at the method level as well as at the type level. At type
level `@MessageMapping` is used to express shared mappings across all methods in a level `@MessageMapping` is used to express shared mappings across all methods in a
controller. controller.
By default destination mappings are expected to be Ant-style, path patterns, e.g. "/foo*", The mapping values are Ant-style path patterns by default, e.g. "/foo*", "/foo/**"
"/foo/**". The patterns include support for template variables, e.g. "/foo/{id}", that can including support for template variables, e.g. "/foo/{id}", that can be referenced via
be referenced with `@DestinationVariable` method arguments. `@DestinationVariable` method arguments. Applications can also switch to a dot-separated
destination convention for mappings, as explained in <<websocket-stomp-destination-separator>>.
[TIP] *Supported Method Arguments*
====
Applications can choose to switch to a dot-separated destination convention.
See <<websocket-stomp-destination-separator>>.
====
`@MessageMapping` methods can have flexible signatures with the following arguments:
[cols="1,2", options="header"] [cols="1,2", options="header"]
|=== |===
@ -1271,47 +1266,52 @@ Values will be converted to the declared method argument type as necessary.
|=== |===
When an `@MessageMapping` method returns a value, by default the value is serialized to *Return Values*
a payload through a configured `MessageConverter`, and then sent as a `Message` to the
`"brokerChannel"` from where it is broadcast to subscribers. The destination of the By default, the return value from an `@MessageMapping` method is serialized to a payload
outbound message is the same as that of the inbound message but prefixed with `"/topic"`. through a matching `MessageConverter`, and sent as a `Message` to the `"brokerChannel"`
from where it is broadcast to subscribers. The destination of the outbound message is the
same as that of the inbound message but prefixed with `"/topic"`.
The `@SendTo` and `@SendToUser` annotations can be used to customize the destination of
the output message. `@SendTo` is used to simply customize target destination, or to
specify multiple destinations. `@SendToUser` is used to direct the output message only
to the user associated with the input message, see <<websocket-stomp-user-destination>>.
You can use the `@SendTo` method annotation to customize the destination to send `@SendTo` and `@SendToUser` may both be used at the same time on the same method, and both
the payload to. `@SendTo` can also be used at the class level to share a default target are supported at the class level in which case they act as a default for methods in the
destination to send messages to. `@SendToUser` is an variant for sending messages only to class. However keep in mind that _any_ method-level `@SendTo` or `@SendToUser` annotations
the user associated with a message. See <<websocket-stomp-user-destination>> for details. override _any_ such annotations at the class level.
The return value from an `@MessageMapping` method may be wrapped with `ListenableFuture`, Messages may be handled asynchronously and a `@MessageMapping` method may return
`CompletableFuture`, or `CompletionStage` in order to produce the payload asynchronously. `ListenableFuture`, `CompletableFuture`, or `CompletionStage`.
As an alternative to returning a payload from an `@MessageMapping` method you can also Note that `@SendTo` and `@SendToUser` are merely a convenience that amounts to using the
send messages using the `SimpMessagingTemplate`, which is also how return values are `SimpMessagingTemplate` to send messages. If necessary, for more advanced scenarios,
handled under the covers. See <<websocket-stomp-handle-send>>. `@MessageMapping` methods can fall back on using the `SimpMessagingTemplate` directly.
This can be done instead of, or possibly in addition to returning a value.
See <<websocket-stomp-handle-send>>.
[[websocket-stomp-subscribe-mapping]] [[websocket-stomp-subscribe-mapping]]
==== `@SubscribeMapping` ==== `@SubscribeMapping`
The `@SubscribeMapping` annotation is used in combination with `@MessageMapping` in order `@SubscribeMapping` is used together with `@MessageMapping` to narrow the mapping to
to narrow the mapping to subscription messages. In such scenarios, the `@MessageMapping` subscription messages. In this scenario `@MessageMapping` expresses message destination
annotation specifies the destination while `@SubscribeMapping` indicates interest in mappings for routing purposes, which can be done at the class or at the method level,
subscription messages only. while `@SubscribeMapping` narrows the mapping to subscription messages only.
An `@SubscribeMapping` method is generally no different from any `@MessageMapping` Methods with `@MessageMapping` and `@SubscribeMapping` support the same
method with respect to mapping and input arguments. For example you can combine it with a <<websocket-stomp-message-mapping,method arguments>> as methods annotated only with
type-level `@MessageMapping` to express a shared destination prefix, and you can use the `@MessageMapping` do. However for the return value, in the absence of `@SendTo` and
same <<websocket-stomp-message-mapping,method arguments>> as any @MessageMapping` method. `@SendToUser`, a message is sent directly as a reply to the subscription, via the
"clientOutboundChannel" channel. Effectively the subscription is used as a one-time,
The key difference with `@SubscribeMapping` is that the return value of the method is request-reply message exchange with the subscription never stored. This is useful for
serialized as a payload and sent, not to the "brokerChannel" but to the loading data on startup and for initializing a front-end UI.
"clientOutboundChannel", effectively replying directly to the client rather than
broadcasting through the broker. This is useful for implementing one-off, request-reply If an `@SubscribeMapping` method is annotated with `@SendTo` and `@SendToUser` the return
message exchanges, and never holding on to the subscription. A common scenario for this value is sent to the `"brokerChannel"` as usual, sending a message subscribers of the
pattern is application initialization when data must be loaded and presented. specified destination(s).
A `@SubscribeMapping` method can also be annotated with `@SendTo` in which case the
return value is sent to the `"brokerChannel"` with the explicitly specified target
destination.
[[websocket-stomp-exception-handler]] [[websocket-stomp-exception-handler]]

Loading…
Cancel
Save