Real‑Time Communication with Spring Boot + WebSocket: Multi‑Client Sessions, Private Chat, and Broadcast
This article demonstrates how to use Spring Boot 3.5.0 with WebSocket + STOMP to build real‑time messaging, covering environment setup, WebSocket configuration, a user‑binding interceptor, and concrete code for private‑user messages, point‑to‑point chats, and global broadcasts, complete with sample results.
1. Introduction
Real‑time scenarios such as system notifications, private chat and global announcements require low‑latency communication. Traditional polling is inefficient, while WebSocket provides a full‑duplex long‑lived connection, making it the optimal solution.
2. Environment
Spring Boot 3.5.0 is used.
3. Configuration
Include the spring-boot-starter-websocket dependency. Define a configuration class:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new StompUserInterceptor());
}
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
// client subscribe prefixes: /topic (broadcast), /queue (point‑to‑point)
config.enableSimpleBroker("/topic", "/queue");
// client send prefix
config.setApplicationDestinationPrefixes("/app");
// user prefix (default /user)
config.setUserDestinationPrefix("/user");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// register endpoint, allow cross‑origin
registry.addEndpoint("/ws").setAllowedOriginPatterns("*").withSockJS();
}
}Define an interceptor that binds a username to the STOMP session:
public class StompUserInterceptor implements ChannelInterceptor {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (accessor == null) {
return message;
}
// When the client initiates CONNECT
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
String username = accessor.getFirstNativeHeader("username");
if (username != null) {
accessor.setUser(new Principal() {
@Override
public String getName() {
return username;
}
});
}
}
return message;
}
}4. Message Sending
4.1 Specific‑User Message
Backend method:
@MessageMapping("/message")
@SendToUser("/queue/replay")
public String chat(String message, Principal principal) {
System.err.println(String.format("[%s] received: %s", principal.getName(), message));
return String.format("Reply: %s", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
}Front‑end (JavaScript):
let username = ...;
const socket = new SockJS(`http://localhost:8080/ws?username=${username}`);
const client = Stomp.over(socket);
const headers = { username };
client.connect(headers, frame => {
client.subscribe('/user/queue/replay', message => {
console.log(`Specific‑user message: ${message.body}`);
});
});4.2 Point‑to‑Point (Private) Message
Backend endpoint:
@GetMapping("/push/user")
@ResponseBody
public String sendToUser(@RequestParam String targetUsername, @RequestParam String msg) {
messagingTemplate.convertAndSendToUser(targetUsername, "/queue/private", msg);
return "Send success";
}Front‑end subscription:
client.subscribe('/user/queue/private', message => {
console.log(`Point‑to‑point message: ${message.body}`);
});4.3 Broadcast Message
Backend method:
@MessageMapping("/broadcast")
@SendTo("/topic/broadcast")
public String broadcast(String message) {
return String.format("%s", message);
}Front‑end subscription:
client.subscribe('/topic/broadcast', message => {
console.log(`Broadcast message: ${message.body}`);
});5. Results
Screenshots show that multiple browsers logged in with the same username receive the same messages (multi‑client session). Separate users only receive messages addressed to them, while broadcast messages are delivered to every connected client.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
