|
对于最新的稳定版本,请使用 Spring Framework 7.0.6! |
注解式控制器
应用程序可以使用带注释的 @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目标,控制器可以对此订阅返回一个值,而无需涉及代理,也不需要再次存储或使用该订阅(有效的一次性请求-响应交换)。此用例之一是在启动时用初始数据填充用户界面。
什么时候这种情况没有用?除非你希望代理和控制器独立处理消息(包括订阅),否则不要尝试将代理和控制器映射到相同的目录取决。传入的消息是并行处理的。不能保证代理或控制器先处理某条消息。如果目标是在订阅存储并准备好广播时收到通知,客户端应请求收据(如果服务器支持的话,简单代理不支持)。例如,使用 Java STOMP 客户端,你可以执行以下操作来添加收据:
@Autowired
private TaskScheduler messageBrokerTaskScheduler;
// During initialization..
stompClient.setTaskScheduler(this.messageBrokerTaskScheduler);
// When subscribing..
StompHeaders headers = new StompHeaders();
headers.setDestination("/topic/...");
headers.setReceipt("r1");
FrameHandler handler = ...;
stompSession.subscribe(headers, handler).addReceiptTask(receiptHeaders -> {
// Subscription ready...
});
服务器端选项是 注册 一个
ExecutorChannelInterceptor 在 brokerChannel 上并实现 afterMessageHandled
在消息(包括订阅)处理之后调用的方法。
@MessageExceptionHandler
应用程序可以使用 @MessageExceptionHandler 种方法来处理来自 @MessageMapping 方法的异常。您可以在注解本身中声明异常,或者通过方法参数声明,如果您想访问异常实例的话。
下面的示例通过方法参数声明了一个异常:
@Controller
public class MyController {
// ...
@MessageExceptionHandler
public ApplicationError handleException(MyException exception) {
// ...
return appError;
}
}
@MessageExceptionHandler 方法支持灵活的方法签名,并支持与
@MessageMapping 方法相同的方法参数类型和返回值。
通常,@MessageExceptionHandler 方法在其声明的 @Controller 类(或类层次结构)中适用。如果您希望这些方法更全局地应用(跨控制器),可以将它们声明在一个用 @ControllerAdvice 标记的类中。这类似于 Spring MVC 中提供的 类似支持。