带注释的控制器
应用程序可以使用带注解的 @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;
// 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 中 类似的支持 相当。