Skip to content

Commit ddb2bdf

Browse files
committed
Improve docs on @controller methods in STOMP section
Issue: SPR-16631
1 parent b9ebdaa commit ddb2bdf

File tree

1 file changed

+128
-64
lines changed

1 file changed

+128
-64
lines changed

src/asciidoc/web-websocket.adoc

Lines changed: 128 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1372,82 +1372,146 @@ kinds of arguments and return values supported.
13721372

13731373

13741374
[[websocket-stomp-handle-annotations]]
1375-
=== Handler methods
1375+
=== Annotated Controllers
13761376

1377-
The `@MessageMapping` annotation is supported on methods of `@Controller` classes.
1378-
It can be used for mapping methods to message destinations and can also be combined
1379-
with the type-level `@MessageMapping` for expressing shared mappings across all
1380-
annotated methods within a controller.
1377+
Applications can use annotated `@Controller` classes to handle messages from clients.
1378+
Such classes can declare `@MessageMapping`, `@SubscribeMapping`, and `@ExceptionHandler`
1379+
methods as described next.
13811380

1382-
By default destination mappings are treated as Ant-style, slash-separated, path
1383-
patterns, e.g. "/foo*", "/foo/**". etc. They can also contain template variables,
1384-
e.g. "/foo/{id}" that can then be referenced via `@DestinationVariable`-annotated
1385-
method arguments.
13861381

1387-
[NOTE]
1382+
[[websocket-stomp-message-mapping]]
1383+
==== `@MessageMapping`
1384+
1385+
The `@MessageMapping` annotation can be used on methods to route messages based on their
1386+
destination. It is supported at the method level as well as at the type level. At type
1387+
level `@MessageMapping` is used to express shared mappings across all methods in a
1388+
controller.
1389+
1390+
By default destination mappings are expected to be Ant-style, path patterns, e.g. "/foo*",
1391+
"/foo/**". The patterns include support for template variables, e.g. "/foo/{id}", that can
1392+
be referenced with `@DestinationVariable` method arguments.
1393+
1394+
[TIP]
13881395
====
1389-
Applications can also use dot-separated destinations (vs slash).
1396+
Applications can choose to switch to a dot-separated destination convention.
13901397
See <<websocket-stomp-destination-separator>>.
13911398
====
13921399

1393-
The following method arguments are supported for `@MessageMapping` methods:
1394-
1395-
* `Message` method argument to get access to the complete message being processed.
1396-
* `@Payload`-annotated argument for access to the payload of a message, converted with
1397-
a `org.springframework.messaging.converter.MessageConverter`.
1398-
The presence of the annotation is not required since it is assumed by default.
1399-
Payload method arguments annotated with validation annotations (like `@Validated`) will
1400-
be subject to JSR-303 validation.
1401-
* `@Header`-annotated arguments for access to a specific header value along with
1402-
type conversion using an `org.springframework.core.convert.converter.Converter`
1403-
if necessary.
1404-
* `@Headers`-annotated method argument that must also be assignable to `java.util.Map`
1405-
for access to all headers in the message.
1406-
* `MessageHeaders` method argument for getting access to a map of all headers.
1407-
* `MessageHeaderAccessor`, `SimpMessageHeaderAccessor`, or `StompHeaderAccessor`
1408-
for access to headers via typed accessor methods.
1409-
* `@DestinationVariable`-annotated arguments for access to template
1410-
variables extracted from the message destination. Values will be converted to
1411-
the declared method argument type as necessary.
1412-
* `java.security.Principal` method arguments reflecting the user logged in at
1413-
the time of the WebSocket HTTP handshake.
1414-
1415-
A return value from an `@MessageMapping` method will be converted with a
1416-
`org.springframework.messaging.converter.MessageConverter` and used as the body
1417-
of a new message that is then sent, by default, to the `"brokerChannel"` with
1418-
the same destination as the client message but using the prefix `"/topic"` by
1419-
default. An `@SendTo` message level annotation can be used to specify any
1420-
other destination instead. It can also be set a class-level to share a common
1400+
`@MessageMapping` methods can have flexible signatures with the following arguments:
1401+
1402+
[cols="1,2", options="header"]
1403+
|===
1404+
| Method argument | Description
1405+
1406+
| `Message`
1407+
| For access to the complete message.
1408+
1409+
| `MessageHeaders`
1410+
| For access to the headers within the `Message`.
1411+
1412+
| `MessageHeaderAccessor`, `SimpMessageHeaderAccessor`, `StompHeaderAccessor`
1413+
| For access to the headers via typed accessor methods.
1414+
1415+
| `@Payload`
1416+
| For access to the payload of the message, converted (e.g. from JSON) via a configured
1417+
`MessageConverter`.
1418+
1419+
The presence of this annotation is not required since it is assumed by default if no
1420+
other argument is matched.
1421+
1422+
Payload arguments may be annotated with `@javax.validation.Valid` or Spring's `@Validated`
1423+
in order to be automatically validated.
1424+
1425+
| `@Header`
1426+
| For access to a specific header value along with type conversion using an
1427+
`org.springframework.core.convert.converter.Converter` if necessary.
1428+
1429+
| `@Headers`
1430+
| For access to all headers in the message. This argument must be assignable to
1431+
`java.util.Map`.
1432+
1433+
| `@DestinationVariable`
1434+
| For access to template variables extracted from the message destination.
1435+
Values will be converted to the declared method argument type as necessary.
1436+
1437+
| `java.security.Principal`
1438+
| Reflects the user logged in at the time of the WebSocket HTTP handshake.
1439+
1440+
|===
1441+
1442+
When an `@MessageMapping` method returns a value, by default the value is serialized to
1443+
a payload through a configured `MessageConverter`, and then sent as a `Message` to the
1444+
`"brokerChannel"` from where it is broadcast to subscribers. The destination of the
1445+
outbound message is the same as that of the inbound message but prefixed with `"/topic"`.
1446+
1447+
You can use the `@SendTo` method annotation to customize the destination to send
1448+
the payload to. `@SendTo` can also be used at the class level to share a default target
1449+
destination to send messages to. `@SendToUser` is an variant for sending messages only to
1450+
the user associated with a message. See <<websocket-stomp-user-destination>> for details.
1451+
1452+
The return value from an `@MessageMapping` method may be wrapped with `ListenableFuture`,
1453+
`CompletableFuture`, or `CompletionStage` in order to produce the payload asynchronously.
1454+
1455+
As an alternative to returning a payload from an `@MessageMapping` method you can also
1456+
send messages using the `SimpMessagingTemplate`, which is also how return values are
1457+
handled under the covers. See <<websocket-stomp-handle-send>>.
1458+
1459+
1460+
[[websocket-stomp-subscribe-mapping]]
1461+
==== `@SubscribeMapping`
1462+
1463+
The `@SubscribeMapping` annotation is used in combination with `@MessageMapping` in order
1464+
to narrow the mapping to subscription messages. In such scenarios, the `@MessageMapping`
1465+
annotation specifies the destination while `@SubscribeMapping` indicates interest in
1466+
subscription messages only.
1467+
1468+
An `@SubscribeMapping` method is generally no different from any `@MessageMapping`
1469+
method with respect to mapping and input arguments. For example you can combine it with a
1470+
type-level `@MessageMapping` to express a shared destination prefix, and you can use the
1471+
same <<websocket-stomp-message-mapping,method arguments>> as on any @MessageMapping` method.
1472+
1473+
The key difference with `@SubscribeMapping` is that the return value of the method is
1474+
serialized as a payload and sent, not to the "brokerChannel" but to the
1475+
"clientOutboundChannel", effectively replying directly to the client rather than
1476+
broadcasting through the broker. This is useful for implementing one-off, request-reply
1477+
message exchanges, and never holding on to the subscription. A common scenario for this
1478+
pattern is application initialization when data must be loaded and presented.
1479+
1480+
A `@SubscribeMapping` method can also be annotated with `@SendTo` in which case the
1481+
return value is sent to the `"brokerChannel"` with the explicitly specified target
14211482
destination.
14221483

1423-
A response message may also be provided asynchronously via a `ListenableFuture`
1424-
or `CompletableFuture`/`CompletionStage` return type signature, analogous to
1425-
deferred results in an MVC handler method.
14261484

1427-
A `@SubscribeMapping` annotation can be used to map subscription requests to
1428-
`@Controller` methods. It is supported on the method level, but can also be
1429-
combined with a type level `@MessageMapping` annotation that expresses shared
1430-
mappings across all message handling methods within the same controller.
1485+
[[websocket-stomp-exception-handler]]
1486+
==== `@MessageExceptionHandler`
14311487

1432-
By default the return value from an `@SubscribeMapping` method is sent as a
1433-
message directly back to the connected client and does not pass through the
1434-
broker. This is useful for implementing request-reply message interactions; for
1435-
example, to fetch application data when the application UI is being initialized.
1436-
Or alternatively an `@SubscribeMapping` method can be annotated with `@SendTo`
1437-
in which case the resulting message is sent to the `"brokerChannel"` using
1438-
the specified target destination.
1488+
An application can use `@MessageExceptionHandler` methods to handle exceptions from
1489+
`@MessageMapping` methods. Exceptions of interest can be declared in the annotation
1490+
itself, or through a method argument if you want to get access to the exception instance:
14391491

1440-
[NOTE]
1441-
====
1442-
In some cases a controller may need to be decorated with an AOP proxy at runtime.
1443-
One example is if you choose to have `@Transactional` annotations directly on the
1444-
controller. When this is the case, for controllers specifically, we recommend
1445-
using class-based proxying. This is typically the default choice with controllers.
1446-
However if a controller must implement an interface that is not a Spring Context
1447-
callback (e.g. `InitializingBean`, `*Aware`, etc), you may need to explicitly
1448-
configure class-based proxying. For example with `<tx:annotation-driven />`,
1449-
change to `<tx:annotation-driven proxy-target-class="true" />`.
1450-
====
1492+
[source,java,indent=0]
1493+
[subs="verbatim,quotes"]
1494+
----
1495+
@Controller
1496+
public class MyController {
1497+
1498+
// ...
1499+
1500+
@MessageExceptionHandler
1501+
public ApplicationError handleException(MyException exception) {
1502+
// ...
1503+
return appError;
1504+
}
1505+
}
1506+
----
1507+
1508+
`@MessageExceptionHandler` methods support flexible method signatures and support the same
1509+
method argument types and return values as <<websocket-stomp-message-mapping>> methods.
1510+
1511+
Typically `@MessageExceptionHandler` methods apply within the `@Controller` class (or
1512+
class hierarchy) they are declared in. If you want such methods to apply more globally,
1513+
across controllers, you can declare them in a class marked with `@ControllerAdvice`.
1514+
This is comparable to <<web.adoc#mvc-ann-controller-advice,similar support>> in Spring MVC.
14511515

14521516

14531517

0 commit comments

Comments
 (0)