注解控制器
应用程序可以使用注解的@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中提供的类似支持。