WebSocket 与 STOMP 基础(spring-websocket)
HTTP 一问一答,适合请求响应;聊天、通知、实时看板需要服务端主动推,WebSocket 提供全双工长连接。spring-websocket 在 Spring MVC 应用里集成 WebSocket,STOMP 则给它加上「话题订阅」的消息语义。
依赖与容器要求
需要 spring-websocket(原生握手)与 spring-messaging(STOMP)两个模块,版本与 spring-webmvc 保持一致;容器需支持 WebSocket(如 Tomcat 9.0+、Jetty 9.4+),具体对应关系以官方文档为准。
方式一:原生 WebSocketHandler
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new ChatHandler(), "/ws/chat")
.setAllowedOriginPatterns("*"); // 跨域放行;生产按源收紧
}
}
处理器继承 TextWebSocketHandler,四个生命周期回调:
public class ChatHandler extends TextWebSocketHandler {
@Override
public void afterConnectionEstablished(WebSocketSession session) {
System.out.println("连接建立:" + session.getId());
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
// 收到客户端文本:echo 回去
session.sendMessage(new TextMessage("echo: " + message.getPayload()));
}
}
方式二:STOMP——给消息加「频道」
STOMP 是基于帧的消息协议,把连接抽象成:客户端向某个「目的地」发送(send),或订阅某个目的地(subscribe)接收推送。Spring 用 @EnableWebSocketMessageBroker 集成:
@Configuration
@EnableWebSocketMessageBroker
public class StompConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws") // 握手端点
.setAllowedOriginPatterns("*")
.withSockJS(); // 浏览器降级方案(SockJS)
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic", "/queue"); // 服务端广播/点对点前缀
registry.setApplicationDestinationPrefixes("/app"); // 客户端发给服务端的前缀
}
}
服务端接收与推送
@Controller
public class ChatController {
@MessageMapping("/chat") // 客户端发往 /app/chat
@SendTo("/topic/messages") // 广播给订阅 /topic/messages 的所有人
public ChatMessage chat(ChatMessage msg) {
return new ChatMessage(msg.getFrom(), msg.getText());
}
}
需要主动推送(非应答式)时注入 SimpMessagingTemplate:
@Service
public class NotifyService {
private final SimpMessagingTemplate template;
public NotifyService(SimpMessagingTemplate template) {
this.template = template; // 由框架自动注入
}
public void notifyAll(String text) {
template.convertAndSend("/topic/notices", new Notice(text)); // 广播
}
}
客户端最小示例(JS)
var socket = new SockJS('https://example.com/ws'); // 换成真实地址
var stomp = Stomp.over(socket);
stomp.connect({}, function () {
stomp.subscribe('/topic/messages', function (frame) { /* 收消息 */ });
stomp.send('/app/chat', {}, JSON.stringify({ from: '张三', text: '你好' }));
});
进阶方向
- 点对点:@SendToUser 配合队列前缀,实现「只推给某个人」。
- 完整消息代理:enableSimpleBroker 适合小规模;大规模生产可接 RabbitMQ/ActiveMQ 的外部 STOMP Broker(enableStompBrokerRelay)。
WebSocket 解决「连接」,STOMP 解决「频道与订阅」。小项目用简单 Broker + @MessageMapping/@SendTo 就够;要广播通知、聊天室、实时看板,先把「端点、前缀、目的地」三个概念理清,骨架搭好后都是往 /topic 或 /queue 上发消息的事。