对于最新的稳定版本,请使用 Spring Framework 7.0.6!spring-doc.cadn.net.cn

注解式控制器

应用程序可以使用带注释的 @Controller 类来处理来自客户端的消息。 此类可以声明 @MessageMapping@SubscribeMapping@ExceptionHandler 方法,如以下主题所述:spring-doc.cadn.net.cn

@MessageMapping

您可以使用 @MessageMapping 来注解根据消息目标路由方法。它在方法级别以及类型级别都受支持。在类型级别,@MessageMapping 用于表达控制器中所有方法之间的共享映射。spring-doc.cadn.net.cn

默认情况下,映射值是 Ant 风格的路径模式(例如 /thing*, /thing/**), 包括对模板变量的支持(例如,/thing/{id})。可以通过 @DestinationVariable 方法参数引用这些值。应用程序还可以切换到以点分隔的目标约定来映射,如 点作为分隔符 中所述。spring-doc.cadn.net.cn

支持的方法参数

下表描述了方法参数:spring-doc.cadn.net.cn

方法参数 描述

Messagespring-doc.cadn.net.cn

要访问完整消息。spring-doc.cadn.net.cn

MessageHeadersspring-doc.cadn.net.cn

访问 Message 中的标头。spring-doc.cadn.net.cn

MessageHeaderAccessor, SimpMessageHeaderAccessor, 和 StompHeaderAccessorspring-doc.cadn.net.cn

通过类型化访问器方法访问标头。spring-doc.cadn.net.cn

@Payloadspring-doc.cadn.net.cn

要访问消息的有效负载,已通过配置的 MessageConverter 转换(例如,从 JSON)。spring-doc.cadn.net.cn

此注解的出现并非必需,因为如果没有其他参数匹配,默认情况下会将其视为已存在。spring-doc.cadn.net.cn

您可以使用 @jakarta.validation.Valid 或 Spring 的 @Validated 对有效负载参数进行注解, 以使有效负载参数自动进行验证。spring-doc.cadn.net.cn

@Headerspring-doc.cadn.net.cn

要访问特定的头值 — 如果需要,可以使用 org.springframework.core.convert.converter.Converter 进行类型转换。spring-doc.cadn.net.cn

@Headersspring-doc.cadn.net.cn

用于访问消息中的所有头信息。此参数必须可分配给 java.util.Mapspring-doc.cadn.net.cn

@DestinationVariablespring-doc.cadn.net.cn

对于访问从消息目标中提取的模板变量。 值会根据声明的方法参数类型进行转换。spring-doc.cadn.net.cn

java.security.Principalspring-doc.cadn.net.cn

在 WebSocket HTTP 握手时的已登录用户。spring-doc.cadn.net.cn

返回值

默认情况下,来自 @MessageMapping 方法的返回值会通过匹配的 MessageConverter 序列化为负载,并作为 Message 发送到 brokerChannel, 从那里它会被广播给订阅者。出站消息的目标与入站消息的目标相同,但前面带有 /topicspring-doc.cadn.net.cn

您可以使用 @SendTo@SendToUser 注解来自定义输出消息的目标。@SendTo 用于自定义目标目的地或指定多个目的地。@SendToUser 用于将输出消息仅定向到与输入消息关联的用户。参见 用户目标spring-doc.cadn.net.cn

您可以同时在同一个方法中使用 @SendTo@SendToUser,并且两者都在类级别上受支持,在这种情况下,它们作为该类中方法的默认值。但是,请注意,任何方法级别的 @SendTo@SendToUser 注解都会覆盖类级别的任何此类注解。spring-doc.cadn.net.cn

消息可以异步处理,并且 @MessageMapping 方法可以返回 ListenableFutureCompletableFutureCompletionStagespring-doc.cadn.net.cn

请注意,@SendTo@SendToUser 只是方便的用法,实际上相当于使用 SimpMessagingTemplate 发送消息。如果需要,对于更高级的场景,@MessageMapping 方法可以回退到直接使用 SimpMessagingTemplate。 这可以代替返回一个值,或者可能同时进行。 参见 发送消息spring-doc.cadn.net.cn

@SubscribeMapping

@SubscribeMapping@MessageMapping 类似,但将映射限制为仅订阅消息。它支持与 @MessageMapping 相同的 方法参数。然而,对于返回值,默认情况下,消息会直接发送给客户端(通过 clientOutboundChannel,作为对订阅的响应),而不是发送给代理服务器(通过 brokerChannel,作为匹配订阅的广播)。添加 @SendTo@SendToUser 会覆盖此行为,而是发送给代理服务器。spring-doc.cadn.net.cn

什么时候会用到这个?假设代理被映射到/topic/queue,而应用程序控制器被映射到/app。在这种设置下,代理将所有针对重复广播的订阅存储到/topic/queue,应用程序无需参与。客户端也可以订阅某些/app目标,控制器可以对此订阅返回一个值,而无需涉及代理,也不需要再次存储或使用该订阅(有效的一次性请求-响应交换)。此用例之一是在启动时用初始数据填充用户界面。spring-doc.cadn.net.cn

什么时候这种情况没有用?除非你希望代理和控制器独立处理消息(包括订阅),否则不要尝试将代理和控制器映射到相同的目录取决。传入的消息是并行处理的。不能保证代理或控制器先处理某条消息。如果目标是在订阅存储并准备好广播时收到通知,客户端应请求收据(如果服务器支持的话,简单代理不支持)。例如,使用 Java STOMP 客户端,你可以执行以下操作来添加收据:spring-doc.cadn.net.cn

@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...
});

服务器端选项是 注册 一个 ExecutorChannelInterceptorbrokerChannel 上并实现 afterMessageHandled 在消息(包括订阅)处理之后调用的方法。spring-doc.cadn.net.cn

@MessageExceptionHandler

应用程序可以使用 @MessageExceptionHandler 种方法来处理来自 @MessageMapping 方法的异常。您可以在注解本身中声明异常,或者通过方法参数声明,如果您想访问异常实例的话。 下面的示例通过方法参数声明了一个异常:spring-doc.cadn.net.cn

@Controller
public class MyController {

	// ...

	@MessageExceptionHandler
	public ApplicationError handleException(MyException exception) {
		// ...
		return appError;
	}
}

@MessageExceptionHandler 方法支持灵活的方法签名,并支持与 @MessageMapping 方法相同的方法参数类型和返回值。spring-doc.cadn.net.cn

通常,@MessageExceptionHandler 方法在其声明的 @Controller 类(或类层次结构)中适用。如果您希望这些方法更全局地应用(跨控制器),可以将它们声明在一个用 @ControllerAdvice 标记的类中。这类似于 Spring MVC 中提供的 类似支持spring-doc.cadn.net.cn