注解控制器
应用程序可以使用注解的@Controller类来处理来自客户端的消息。这些类可以声明@MessageMapping、@SubscribeMapping和@ExceptionHandler方法,如下面的主题所述:
@MessageMapping
您可以使用@MessageMapping来注解路由消息的方法,基于它们的目的地。它支持方法级别以及类型级别。在类型级别,@MessageMapping用于表示控制器中所有方法的共享映射。
默认情况下,映射值是Ant风格的路径模式(例如/thing*,/thing/**),包括对模板变量的支持(例如/thing/{id})。这些值可以通过@DestinationVariable方法参数引用。应用程序还可以切换到使用点分隔符约定进行映射,如点作为分隔符中所述。
支持的方法参数
以下表格描述了方法参数:
| 方法参数 | 描述 |
|---|---|
|
|
用于访问完整消息。 |
|
|
用于访问 |
|
|
通过类型化的访问器方法访问标头。 |
|
|
用于访问消息的有效载荷,通过配置的 您可以使用 |
|
|
用于访问特定标头值 —— 并在必要时使用 |
|
|
用于访问消息中的所有标头。此参数必须可分配给 |
|
|
用于访问从消息目的地提取的模板变量。值将根据需要转换为声明的方法参数类型。 |
|
|
反映在WebSocket HTTP握手时登录的用户。 |
返回值
默认情况下,从@MessageMapping方法返回的值通过匹配的MessageConverter序列化为有效载荷,并作为Message发送到brokerChannel,然后广播给订阅者。出站消息的目的地与入站消息相同,但前缀为/topic。
您可以使用@SendTo和@SendToUser注解来自定义输出消息的目的地。@SendTo用于自定义目标目的地或指定多个目的地。@SendToUser用于将输出消息仅发送给与输入消息关联的用户。请参阅用户目的地。
您可以同时在同一方法上使用@SendTo和@SendToUser,并且两者都支持在类级别使用,这种情况下它们将作为类中方法的默认值。但请注意,任何方法级别的@SendTo或@SendToUser注解将覆盖类级别的任何此类注解。
消息可以异步处理,@MessageMapping方法可以返回ListenableFuture、CompletableFuture或CompletionStage。
请注意,@SendTo和@SendToUser仅仅是一种方便的方式,相当于使用SimpMessagingTemplate发送消息。必要时,对于更高级的场景,@MessageMapping方法可以回退到直接使用SimpMessagingTemplate。这可以代替或可能与返回值一起使用。请参阅发送消息。
@SubscribeMapping
@SubscribeMapping类似于@MessageMapping,但将映射限制为仅订阅消息。它支持与@MessageMapping相同的方法参数。但是对于返回值,默认情况下,消息直接发送到客户端(通过clientOutboundChannel,作为对订阅的响应),而不是发送到代理(通过brokerChannel,作为匹配订阅的广播)。添加@SendTo或@SendToUser会覆盖此行为并发送到代理。
这在什么情况下有用?假设代理映射到/topic和/queue,而应用程序控制器映射到/app。在这种设置中,代理存储所有订阅/topic和/queue的订阅,这些订阅用于重复广播,并且应用程序无需参与。客户端还可以订阅一些/app目的地,控制器可以在响应该订阅时返回一个值,而无需涉及代理,也不存储或再次使用该订阅(实际上是一次性请求-回复交换)。一个用例是在启动时使用初始数据填充UI。
这在什么情况下不有用?不要尝试将代理和控制器映射到相同的目的地前缀,除非出于某种原因希望两者独立处理消息,包括订阅。入站消息是并行处理的。不能保证代理或控制器哪个先处理给定的消息。如果目标是在订阅存储并准备进行广播时收到通知,客户端应该在服务器支持时请求回执(简单代理不支持)。例如,使用Java STOMP客户端,您可以执行以下操作添加回执:
@Autowired
private TaskScheduler messageBrokerTaskScheduler;
// 初始化时..
stompClient.setTaskScheduler(this.messageBrokerTaskScheduler);
// 订阅时..
StompHeaders headers = new StompHeaders();
headers.setDestination("/topic/...");
headers.setReceipt("r1");
FrameHandler handler = ...;
stompSession.subscribe(headers, handler).addReceiptTask(receiptHeaders -> {
// 订阅准备就绪...
});
服务器端选项是在brokerChannel上注册ExecutorChannelInterceptor并实现在处理消息(包括订阅)后调用的afterMessageHandled方法。
@MessageExceptionHandler
应用程序可以使用@MessageExceptionHandler方法来处理@MessageMapping方法抛出的异常。如果希望访问异常实例,可以在注解本身或通过方法参数中声明异常。以下示例通过方法参数声明异常:
@Controller
public class MyController {
// ...
@MessageExceptionHandler
public ApplicationError handleException(MyException exception) {
// ...
return appError;
}
}
@MessageExceptionHandler方法支持灵活的方法签名,并支持与@MessageMapping方法相同的方法参数类型和返回值。
通常,@MessageExceptionHandler方法适用于声明它们的@Controller类(或类层次结构)内。如果希望这些方法在更广泛的范围内适用(跨控制器),可以在标记有@ControllerAdvice的类中声明它们。这类似于Spring MVC中提供的类似支持。