Spring Websockets 的 @SendToUser 注解
1、概览
本文将带你了解如何在 Spring WebSockets 中使用 @SendToUser 注解向特定 Session 或特定用户发送消息。
有关上述 Spring WebSockets 的介绍,请参阅 上一篇文章。
2、WebSocket 配置
首先,需要配置 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
获取 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 端点
从 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 客户端
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、总结
本文介绍了如何在 Spring WebSocket 中使用 @SendToUser 注解直接向用户或 Session ID 发送消息。
Ref:https://www.baeldung.com/spring-websockets-sendtouser
 
				