WebSocket with Spring Boot

1 minute read

WebSocket enables real-time, bidirectional communication — chat, live notifications, real-time dashboards.


Setup

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

WebSocket Configuration

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic", "/queue");  // server → client
        config.setApplicationDestinationPrefixes("/app");        // client → server
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws")
            .setAllowedOrigins("http://localhost:3000")
            .withSockJS();     // fallback for old browsers
    }
}

Message Controller

@Controller
public class ChatController {

    @MessageMapping("/chat.send")           // client sends to /app/chat.send
    @SendTo("/topic/messages")              // broadcast to all subscribers
    public ChatMessage sendMessage(ChatMessage message) {
        return message;
    }

    @MessageMapping("/chat.join")
    @SendTo("/topic/messages")
    public ChatMessage addUser(ChatMessage message, SimpMessageHeaderAccessor headerAccessor) {
        headerAccessor.getSessionAttributes().put("username", message.getSender());
        message.setType(MessageType.JOIN);
        return message;
    }
}

public class ChatMessage {
    private MessageType type;  // CHAT, JOIN, LEAVE
    private String content;
    private String sender;
    private LocalDateTime timestamp;
    // getters, setters
}

public enum MessageType { CHAT, JOIN, LEAVE }

Sending Messages from Services

@Service
public class NotificationService {

    private final SimpMessagingTemplate messagingTemplate;

    public NotificationService(SimpMessagingTemplate messagingTemplate) {
        this.messagingTemplate = messagingTemplate;
    }

    // Broadcast to all subscribers of /topic/notifications
    public void broadcastNotification(String message) {
        messagingTemplate.convertAndSend("/topic/notifications",
            new Notification(message, LocalDateTime.now()));
    }

    // Send to a specific user
    public void sendToUser(String username, String message) {
        messagingTemplate.convertAndSendToUser(
            username, "/queue/private",
            new Notification(message, LocalDateTime.now()));
    }
}

JavaScript Client

const socket = new SockJS('/ws');
const stompClient = Stomp.over(socket);

stompClient.connect({}, function(frame) {
    // Subscribe to broadcast messages
    stompClient.subscribe('/topic/messages', function(message) {
        const chatMessage = JSON.parse(message.body);
        displayMessage(chatMessage);
    });

    // Subscribe to private messages
    stompClient.subscribe('/user/queue/private', function(message) {
        displayPrivateMessage(JSON.parse(message.body));
    });

    // Send a message
    stompClient.send('/app/chat.send', {},
        JSON.stringify({ sender: 'Alice', content: 'Hello!', type: 'CHAT' }));
});

Connection Events

@Component
public class WebSocketEventListener {

    @EventListener
    public void handleConnect(SessionConnectedEvent event) {
        log.info("New WebSocket connection");
    }

    @EventListener
    public void handleDisconnect(SessionDisconnectEvent event) {
        StompHeaderAccessor headers = StompHeaderAccessor.wrap(event.getMessage());
        String username = (String) headers.getSessionAttributes().get("username");
        if (username != null) {
            log.info("User disconnected: {}", username);
            messagingTemplate.convertAndSend("/topic/messages",
                new ChatMessage(MessageType.LEAVE, null, username, LocalDateTime.now()));
        }
    }
}

Key Takeaways

  1. WebSocket = persistent, bidirectional connection (unlike HTTP request-response).
  2. STOMP protocol provides subscribe/publish messaging over WebSocket.
  3. /topic/ for broadcasts, /queue/ for point-to-point, /user/ for user-specific.
  4. SimpMessagingTemplate sends messages from any service.
  5. SockJS provides fallback for browsers without WebSocket support.

What’s Next?

Next: GraphQL with Spring Boot — query exactly what you need.

Updated: