上一篇文章讲了如何进行stomp集成的简易教程,这一篇主要讲两点
- 解释如何给某个人发送消息
- 前端如何通过node基于模块化的方式引入Stomp
开头把详情文档地址放一下:Web on Servlet Stack (spring.io)
第一步:config文件调整
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
/**
* websocket的配置类
* @author dfg
* @date 2023-04-28 21:50
*/
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Autowired
private AuthChannelInterceptor authChannelInterceptor;
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
// 客户端接收服务器消息的地址前缀
config.enableSimpleBroker("/topic");
// 客户端给服务端发消息的地址前缀
config.setApplicationDestinationPrefixes("/app");
}
/**
* 注册端点
* setAllowedOrigins 支持跨域
* withSockJS 支持socketJs
* @param registry
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gs-guide-websocket").setAllowedOriginPatterns("*").withSockJS();
}
/**
* 连接之前注册登陆信息,识别身份
* @param registration
*/
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(authChannelInterceptor);
}
}
添加AuthChannelInterceptor类,通过spring的方式引入,代码如下
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.stereotype.Component;
/**
* 用于控制登陆的通道拦截器
* @author dfg
* @date 2023-05-05 22:40
*/
@Component
@Order(Ordered.HIGHEST_PRECEDENCE + 99)
public class AuthChannelInterceptor implements ChannelInterceptor {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (accessor == null) {
return message;
}
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
// todo 做登陆拦截控制
accessor.setUser(new LoginUser(accessor.getSessionId()));
}
return message;
}
}
/**
* 自定义的用户认证类
* @author dfg
* @date 2023-05-05 22:48
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginUser implements Principal {
/**
* 登陆的id
*/
private String name;
@Override
public String getName() {
return name;
}
}
这个token可以设置成业务系统的uid,这样通过uid就能给某个人发送消息了,之后的文章中出现用户token指的就是此处的token
第二步:监听断开连接事件
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.messaging.SessionDisconnectEvent;
/**
* 监听器
* @author dfg
* Created by on 2023-05-06 22:36
*/
@Service
public class MyListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof SessionDisconnectEvent) {
// 取消连接的消息 打印离开人的id
System.out.println(((SessionDisconnectEvent) event).getSessionId());
}
}
}
监听断开事件通过spring的消息机制实现,所以通过实现ApplicationListener即可完成功能自定义,其他事件查看官方链接自行了解Web on Servlet Stack。
第三步:给指定用户发送消息
1.先认识一下SimpMessagingTemplate
全路径:org.springframework.messaging.simp.SimpMessagingTemplate
上一篇说到的广播形式,底层就是通过这个类进行的消息发送,通过简单调试找到这个位置org.springframework.messaging.simp.annotation.support.SendToMethodReturnValueHandler#handleReturnValue,截图如下

这里再说一下,虽然是通过SimpMessagingTemplate发送的消息,但是真正底层给客户端发送消息的并不是这个类,而是上一篇原理图提到的SimpleBrokerMessageHandler,通过调试找到org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler#sendMessageToSubscribers这个位置,我给大家放一张调试的截图如下

总结一下广播消息的原理:后端维护了一个map结构,key是目的地,value是用户token的链表,广播消息就是找到这个map,拿出value,遍历value拿到用户token,根据用户token跟客户端通道的绑定关系找到通道,通过通道给用户发送消息
2.封装成一个service
为啥要封装呢,因为SimpMessagingTemplate不支持给多人发送消息,封装一下更好用
import java.util.List;
import cn.hutool.core.util.StrUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
/**
* 发送消息
* @author dfg
* Created by on 2023-05-06 21:39
*/
@Service
public class SendMsgServiceImpl implements SendMsgService {
@Autowired
private SimpMessagingTemplate template;
@Override
public void sendMsg(String destination, Object payload) {
if (StrUtil.isBlank(destination) || payload == null) {
return;
}
// 发送消息
template.convertAndSend(destination, payload);
}
@Override
public void sendMsgToUser(String userToken, String destination, Object payload) {
if (StrUtil.isBlank(userToken) || StrUtil.isBlank(destination) || payload == null) {
return;
}
template.convertAndSendToUser(userToken, destination, payload);
}
@Override
public void sendMsgToUsers(List<String> userTokens, String destination, Object payload) {
if (CollectionUtils.isEmpty(userTokens) || StrUtil.isBlank(destination) || payload == null) {
return;
}
// 循环发送消息
userTokens.forEach(x -> template.convertAndSendToUser(x, destination, payload));
}
}
到了这服务端部分已经结束了
第四步:前端通过node使用stomp
第一种:webstomp-client+sockjs-client
这是spring推荐的使用方式,文档地址:Web on Servlet Stack

第一步:下载npm包
npm i --save sockjs-client
npm i --save webstomp-client
第二步:测试连接
import webstomp from 'webstomp-client'
import SockJS from 'sockjs-client/dist/sockjs.min.js'
var socket = new SockJS('http://127.0.0.1:8080/gs-guide-websocket');
var stompClient = webstomp.over(socket)
stompClient.connect({}, frame => {
console.log(frame)
})
这里使用了vite项目以typescript为脚本做了测试,其他webstomp-client用法详见GitHub - JSteunou/webstomp-client: Stomp client over websocket for browsers
第二种:stompjs+sockjs-client
因为webstomp-client挺古老了, 正好在Issus里面看到了stompjs,所以也集成了一下
第一步:下载npm包
npm i --save sockjs-client
npm i --save @stomp/stompjs ws
第二步:测试连接
import SockJS from 'sockjs-client/dist/sockjs.min.js'
import { Client } from '@stomp/stompjs'
var client = new Client()
// 通过sockJS连接
client.webSocketFactory= function () {
return new SockJS("http://127.0.0.1:8080/gs-guide-websocket");
};
// 连接之后打印结果
client.onConnect = frame => {
console.log(frame)
};
// 激活连接
client.activate();
其他stompjs用法,查看用法详见Using StompJs v5+ - StompJS Family
本文介绍了如何在Spring中配置WebSocketSTOMP,包括配置文件、添加拦截器以验证用户身份,以及监听断开连接事件。此外,文章还展示了如何通过SimpMessagingTemplate给指定用户发送消息,并提供前端使用webstomp-client和stompjs集成的例子。
1287

被折叠的 条评论
为什么被折叠?



