WebSocket 实时通信:Spring Boot 实现在线聊天室

James Park | 2026-08-27T20:55:45 | Java, Spring Boot

用 Spring Boot + WebSocket + STOMP 协议实现在线聊天室,涵盖消息广播、私聊和在线用户管理。

# WebSocket 实现在线聊天室 ## WebSocket vs HTTP | 对比 | HTTP | WebSocket | |------|------|-----------| | 连接方式 | 短连接/轮询 | 全双工长连接 | | 方向 | 客户端 -> 服务器 | 双向 | | 头部开销 | 每次请求完整头部 | 首次握手后开销极小 | | 适用场景 | REST API | 聊天、实时通知 | ## Spring Boot 配置 ```java @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { // 广播消息前缀 config.enableSimpleBroker("/topic", "/queue"); // 客户端发送消息前缀 config.setApplicationDestinationPrefixes("/app"); // 点对点消息前缀 config.setUserDestinationPrefix("/user"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws") .setAllowedOriginPatterns("*") .withSockJS(); // 兼容不支持 WebSocket 的浏览器 } } ``` ## 消息控制器 ```java @Controller public class ChatController { private final SimpMessagingTemplate messagingTemplate; public ChatController(SimpMessagingTemplate messagingTemplate) { this.messagingTemplate = messagingTemplate; } // 群发消息 @MessageMapping("/chat.send") @SendTo("/topic/public") public ChatMessage sendMessage(ChatMessage message) { return message; } // 用户加入 @MessageMapping("/chat.join") @SendTo("/topic/public") public ChatMessage addUser(ChatMessage message, SimpMessageHeaderAccessor headerAccessor) { headerAccessor.getSessionAttributes().put("username", message.getSender()); message.setType(ChatMessage.MessageType.JOIN); return message; } // 私聊 @MessageMapping("/chat.private") public void sendPrivateMessage(PrivateMessage message) { messagingTemplate.convertAndSendToUser( message.getRecipient(), "/queue/private", message ); } } ``` ## 在线用户管理 ```java @Component public class WebSocketEventListener { private final Set onlineUsers = ConcurrentHashMap.newKeySet(); private final SimpMessagingTemplate messagingTemplate; @EventListener public void handleWebSocketConnect(SessionConnectedEvent event) { // 用户连接 } @EventListener public void handleWebSocketDisconnect(SessionDisconnectEvent event) { StompHeaderAccessor accessor = StompHeaderAccessor.wrap(event.getMessage()); String username = (String) accessor.getSessionAttributes().get("username"); if (username != null) { onlineUsers.remove(username); ChatMessage leaveMessage = new ChatMessage(); leaveMessage.setType(ChatMessage.MessageType.LEAVE); leaveMessage.setSender(username); messagingTemplate.convertAndSend("/topic/public", leaveMessage); // 广播在线用户列表 messagingTemplate.convertAndSend("/topic/online-users", onlineUsers); } } } ``` ## 前端连接 ```javascript const socket = new SockJS('/ws'); const stompClient = Stomp.over(socket); stompClient.connect({}, function(frame) { console.log('Connected: ' + frame); // 订阅公共频道 stompClient.subscribe('/topic/public', function(message) { const msg = JSON.parse(message.body); displayMessage(msg); }); // 订阅私聊频道 stompClient.subscribe('/user/queue/private', function(message) { const msg = JSON.parse(message.body); displayPrivateMessage(msg); }); // 发送加入消息 stompClient.send('/app/chat.join', {}, JSON.stringify({ sender: username, type: 'JOIN' }) ); }); function sendMessage(content) { stompClient.send('/app/chat.send', {}, JSON.stringify({ sender: username, content: content, type: 'CHAT' }) ); } ``` ## 认证集成 ```java @Override public void configureClientInboundChannel(ChannelRegistration registration) { registration.interceptors(new ChannelInterceptor() { public Message preSend(Message message, MessageChannel channel) { StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message); if (StompCommand.CONNECT.equals(accessor.getCommand())) { String token = accessor.getFirstNativeHeader("Authorization"); // 验证 JWT Token UserDetails user = jwtUtil.parseToken(token); accessor.setUser(new UsernamePasswordAuthenticationToken(user, null)); } return message; } }); } ``` ## 注意事项 1. 生产环境使用外部消息代理(RabbitMQ/Redis)替代内存代理 2. 配置心跳保持连接活跃 3. 多实例部署时通过消息代理同步消息

← Back to Blog