1、概览 {#1概览}
本文将带你了解如何在 Spring WebSockets 中使用 @SendToUser
注解向特定 Session 或特定用户发送消息。
有关上述 Spring WebSockets 的介绍,请参阅 上一篇文章。
2、WebSocket 配置 {#2websocket-配置}
首先,需要配置 Message Broker 和 WebSocket 应用端点:
@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic/", "/queue/"); config.setApplicationDestinationPrefixes("/app"); }
@Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/greeting"); }
}
通过 @EnableWebSocketMessageBroker
注解,启用了在 WebSocket 上使用 STOMP(Streaming Text Oriented Messaging Protocol)的基于 Broker 的消息传递。需要强调的是,这个注解需要与 @Configuration
一起使用。
继承 AbstractWebSocketMessageBrokerConfigurer
并不是必须的,但这可以更容易地自定义导入的配置。
在第一个方法中,建立了一个简单的基于内存的 Message Broker,通过以 /topic
和 /queue
为前缀的目标将消息传回客户端。
第二个方法,注册了 /greeting
stomp 端点。
可以考虑启用 SockJS。
registry.addEndpoint("/greeting").withSockJS();
3、通过 Interceptor 获取 Session ID {#3通过-interceptor-获取-session-id}
获取 Session ID 的一种方法是添加一个 Spring Interceptor,它在握手过程中触发,并从请求数据中获取信息。
此 Interceptor 可直接添加到 WebSocketConfig
中:
@Override public void registerStompEndpoints(StompEndpointRegistry registry) {
registry .addEndpoint("/greeting") .setHandshakeHandler(new DefaultHandshakeHandler() {
public boolean beforeHandshake( ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) throws Exception {
if (request instanceof ServletServerHttpRequest) { ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request; HttpSession session = servletRequest .getServletRequest().getSession(); attributes.put("sessionId", session.getId()); } return true; }}).withSockJS();
}
4、WebSocket 端点 {#4websocket-端点}
从 Spring 5.0.5.RELEASE 开始,无需进行任何自定义,因为 @SendToUser
注解的改进允许我们通过 /user/{sessionId}/...
而不是 /user/{user}/...
向目标用户发送消息。
这意味着该注解依赖于输入消息的 Session ID,从而有效地将回复发送到会话私有的目标位置:
@Controller public class WebSocketController {
@Autowired private SimpMessageSendingOperations messagingTemplate;
private Gson gson = new Gson();
@MessageMapping("/message") @SendToUser("/queue/reply") public String processMessageFromClient( @Payload String message, Principal principal) throws Exception { return gson .fromJson(message, Map.class) .get("name").toString(); }
@MessageExceptionHandler @SendToUser("/queue/errors") public String handleException(Throwable exception) { return exception.getMessage(); }
}
@SendToUser
注解表示消息处理方法的返回值应作为消息发送到指定目标,目标前缀为 /user/{username}
。
5、WebSocket 客户端 {#5websocket-客户端}
function connect() { var socket = new WebSocket('ws://localhost:8080/greeting'); ws = Stomp.over(socket);
ws.connect({}, function(frame) { ws.subscribe("/user/queue/errors", function(message) { alert("Error " + message.body); });
ws.subscribe("/user/queue/reply", function(message) { alert("Message " + message.body); });
}, function(error) { alert("STOMP error " + error); });
}
function disconnect() { if (ws != null) { ws.close(); } setConnected(false); console.log("Disconnected"); }
在 WebSocketConfiguration
中创建了一个新的 WebSocket,将其映射到 /greeting
。
当客户端订阅到 /user/queue/errors
和 /user/queue/reply
时,就会使用上一节中提到的信息。
可以看到,@SendToUser
指向的是 queue/errors
,但消息将被发送到 /user/queue/errors
。
6、总结 {#6总结}
本文介绍了如何在 Spring WebSocket 中使用 @SendToUser
注解直接向用户或 Session ID 发送消息。
Ref:https://www.baeldung.com/spring-websockets-sendtouser