package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
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;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
// Enable simple broker for topics
registry.enableSimpleBroker("/topic", "/queue");
// Prefix for messages from client to server
registry.setApplicationDestinationPrefixes("/app");
// Prefix for user-specific messages
registry.setUserDestinationPrefix("/user");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setAllowedOrigins("http://localhost:3000")
.withSockJS();
}
}
package com.example.demo.controller;
import com.example.demo.dto.ChatMessage;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;
import java.security.Principal;
@Controller
public class ChatController {
private final SimpMessagingTemplate messagingTemplate;
public ChatController(SimpMessagingTemplate messagingTemplate) {
this.messagingTemplate = messagingTemplate;
}
// Broadcast to all subscribers
@MessageMapping("/chat.send")
@SendTo("/topic/public")
public ChatMessage sendMessage(@Payload ChatMessage message) {
return message;
}
// Send to specific user
@MessageMapping("/chat.private")
public void sendPrivateMessage(@Payload ChatMessage message, Principal principal) {
messagingTemplate.convertAndSendToUser(
message.getRecipient(),
"/queue/private",
message
);
}
// Handle user join
@MessageMapping("/chat.join")
@SendTo("/topic/public")
public ChatMessage joinChat(@Payload ChatMessage message,
SimpMessageHeaderAccessor headerAccessor) {
// Add username to session
headerAccessor.getSessionAttributes().put("username", message.getSender());
message.setType(ChatMessage.MessageType.JOIN);
return message;
}
// Send notification to specific user
public void sendNotification(String username, String notification) {
messagingTemplate.convertAndSendToUser(
username,
"/queue/notifications",
notification
);
}
// Broadcast system message
public void broadcastSystemMessage(String message) {
ChatMessage chatMessage = new ChatMessage();
chatMessage.setType(ChatMessage.MessageType.SYSTEM);
chatMessage.setContent(message);
messagingTemplate.convertAndSend("/topic/public", chatMessage);
}
}
package com.example.demo.listener;
import com.example.demo.dto.ChatMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.messaging.SessionConnectedEvent;
import org.springframework.web.socket.messaging.SessionDisconnectEvent;
@Component
public class WebSocketEventListener {
private static final Logger logger = LoggerFactory.getLogger(WebSocketEventListener.class);
private final SimpMessagingTemplate messagingTemplate;
public WebSocketEventListener(SimpMessagingTemplate messagingTemplate) {
this.messagingTemplate = messagingTemplate;
}
@EventListener
public void handleWebSocketConnectListener(SessionConnectedEvent event) {
logger.info("New WebSocket connection");
}
@EventListener
public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) {
StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(event.getMessage());
String username = (String) headerAccessor.getSessionAttributes().get("username");
if (username != null) {
logger.info("User disconnected: {}", username);
ChatMessage message = new ChatMessage();
message.setType(ChatMessage.MessageType.LEAVE);
message.setSender(username);
messagingTemplate.convertAndSend("/topic/public", message);
}
}
}
WebSocket enables full-duplex communication between client and server. Spring WebSocket supports STOMP protocol over WebSocket. I configure message brokers for pub-sub messaging. @MessageMapping handles incoming messages. SimpMessagingTemplate sends messages to clients. Topics broadcast to multiple subscribers; queues send to individual users. Security integrates with Spring Security. SockJS provides fallback for browsers without WebSocket support. WebSocket connections persist, enabling push notifications, live updates, and chat applications. Connection lifecycle hooks manage setup and cleanup. Message conversion supports JSON, binary, and custom formats. WebSocket excels for real-time dashboards, collaborative tools, and gaming. Proper error handling and heartbeat mechanisms ensure connection stability.