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.
Related snips
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
createdAt: String!
GraphQL API with Spring Boot
package com.example.starter.config;
import com.example.starter.properties.CustomProperties;
import com.example.starter.service.CustomService;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
Custom Spring Boot starters
class Comment < ApplicationRecord
belongs_to :article
belongs_to :author, class_name: "User"
validates :body, presence: true, length: { maximum: 2_000 }
Live comments with model broadcasts + turbo_stream_from
<%# private stream: turbo signs the serialized record name %>
<%= turbo_stream_from current_user %>
<section class="notifications">
<h1>Notifications</h1>
Turbo Streams + authorization: signed per-user stream name
package com.example.demo.config;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.apache.kafka.common.serialization.StringSerializer;
Messaging with Apache Kafka
-- Publisher: Send notification
NOTIFY new_order, 'Order #12345 created';
-- Subscriber: Listen for notifications
LISTEN new_order;
PostgreSQL LISTEN/NOTIFY for pub-sub messaging
Share this code
Here's the card — post it anywhere.