skip to content

WebSocket & STOMP Messaging

Pushing to browsers: the raw WebSocket API, STOMP over WebSocket with a broker, and SockJS fallback plus per-user destinations and security. Interviewers ask about it whenever the product has notifications, chat or live dashboards.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

explore

questions

15

What is STOMP over WebSocket in Spring, and what does @EnableWebSocketMessageBroker set up?

level: juniorimportance: must knowfreq 70%

answer

  1. WebSocket = pipe, STOMP = pub/sub on top
  2. @EnableWebSocketMessageBroker + WebSocketMessageBrokerConfigurer
  3. registerStompEndpoints + configureMessageBroker
  4. SUBSCRIBE / SEND / MESSAGE frames
  5. /ws endpoint, withSockJS fallback

basics

~20 s

WebSocket is a raw two-way connection; STOMP is a simple text messaging protocol layered on top that adds destinations (like topics). @EnableWebSocketMessageBroker turns on Spring's message-broker support so clients can subscribe and publish over WebSocket.

solid answer

~40 s

A raw WebSocket only gives you a bidirectional byte/text pipe with no message semantics. STOMP (Simple Text Oriented Messaging Protocol) is a lightweight, frame-based sub-protocol that adds a pub/sub model on top: clients SUBSCRIBE to destinations and SEND to destinations. Spring's @EnableWebSocketMessageBroker (on a @Configuration class implementing WebSocketMessageBrokerConfigurer) activates the full messaging stack: you register the STOMP endpoint clients connect to (registerStompEndpoints), and configure the message broker and destination prefixes (configureMessageBroker). This lets you write @MessageMapping controller methods that handle incoming messages and route outgoing ones to a broker, rather than manually managing raw WebSocketHandler callbacks. It's the higher-level programming model, analogous to how @RestController sits above raw servlets.

code

java · 15 lines
java
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws").withSockJS();
    }

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

go deeper

for a junior

Know that WebSocket is the transport and STOMP adds pub/sub semantics; @EnableWebSocketMessageBroker turns it on.

for a middle

Be able to write the config class with both override methods and explain the client message flow.

for a senior

Explain how the annotation handler, channels, and broker wire together, and the security implications of per-frame authorization.

for a principal

Weigh STOMP-over-WebSocket vs SSE vs raw WebSocket, and factor in the operational cost of the messaging stack for the use case.

### The layers **WebSocket** is a transport: after an HTTP handshake it upgrades the connection to a persistent, full-duplex TCP channel over which either side can push text or binary frames at any time. But WebSocket by itself defines *no application semantics* — no concept of a 'topic', 'subscribe', or message routing. If you used Spring's raw `WebSocketHandler`, you'd have to invent your own message format and routing. **STOMP** (Simple/Streaming Text Oriented Messaging Protocol) is a simple, broker-neutral sub-protocol negotiated during the WebSocket handshake (`Sec-WebSocket-Protocol: v12.stomp`). It gives you a small set of frame commands — `CONNECT`, `SUBSCRIBE`, `SEND`, `MESSAGE`, `UNSUBSCRIBE`, `ACK`, `DISCONNECT` — each with a command line, headers (including a `destination`), and a body. This makes the connection a **pub/sub messaging channel**: clients subscribe to destinations like `/topic/prices` and send to destinations like `/app/greeting`. ### What @EnableWebSocketMessageBroker does Put it on a `@Configuration` class that implements `WebSocketMessageBrokerConfigurer`. It imports Spring's messaging infrastructure and expects you to override two methods: - `registerStompEndpoints(StompEndpointRegistry)` — declares the HTTP URL where clients open the WebSocket + STOMP connection, e.g. `registry.addEndpoint("/ws").withSockJS()`. `withSockJS()` adds a fallback for browsers/proxies that can't do native WebSocket. - `configureMessageBroker(MessageBrokerRegistry)` — declares destination prefixes and which broker handles them (see below). Once enabled, Spring wires up: a `clientInboundChannel` (messages from clients), a `SimpAnnotationMethodMessageHandler` that dispatches to your `@MessageMapping` methods, the broker (simple in-memory or a relay to RabbitMQ/ActiveMQ), and a `clientOutboundChannel` (messages back to clients). ### The message flow 1. Client opens WebSocket to `/ws`, sends STOMP `CONNECT`. 2. Client `SUBSCRIBE`s to `/topic/greetings`. 3. Client `SEND`s to `/app/hello`. 4. Spring routes `/app/**` messages to your `@MessageMapping("/hello")` controller method (application destinations). 5. The method's return value (with `@SendTo("/topic/greetings")`) is published to the broker, which fans it out to all subscribers. ### When to use Use STOMP-over-WebSocket for interactive, low-latency, multi-user push: chat, live dashboards, notifications, collaborative editing, live sports/price feeds. If you only need occasional server-to-client push and not a full pub/sub model, Server-Sent Events (SSE) may be simpler. ### Gotchas - You still need Spring Security config for the WebSocket handshake and message authorization — the normal HTTP filter chain only covers the handshake, not individual STOMP frames. - SockJS and native WebSocket have different endpoint URLs/behaviors; the client library must match.

  • What does withSockJS() add and why would you need it?
    It enables the SockJS fallback protocol, so clients behind proxies or in browsers without native WebSocket support can still get a WebSocket-like connection via HTTP streaming/long-polling transports.
  • Is STOMP required to use WebSocket in Spring?
    No. You can implement a raw WebSocketHandler and define your own message format. STOMP is the higher-level, broker-backed option that gives you pub/sub routing and @MessageMapping controllers for free.

context

open as a page

How do you set up a raw (low-level) WebSocket endpoint in Spring, without STOMP?

level: juniorimportance: must knowfreq 55%

basics

~10 s

Add @EnableWebSocket on a config class that implements WebSocketConfigurer, then override registerWebSocketHandlers to map your handler (a TextWebSocketHandler subclass) to a URL path with registry.addHandler(handler, "/ws").

open as a page

Explain application vs broker destination prefixes and how @MessageMapping / @SendTo route messages.

level: middleimportance: must knowfreq 68%

basics

~20 s

Application prefixes (like /app) route incoming client messages to your @MessageMapping controller methods. Broker prefixes (like /topic, /queue) go straight to the broker for subscription/fan-out. @SendTo names the broker destination the method's return value is published to.

open as a page

Walk through the lifecycle callbacks of a WebSocketHandler / TextWebSocketHandler.

level: middleimportance: must knowfreq 50%

basics

~20 s

afterConnectionEstablished runs when a client connects; handleMessage (or handleTextMessage in TextWebSocketHandler) runs per incoming frame; handleTransportError on I/O errors; afterConnectionClosed when the socket closes. You typically track sessions in the first and remove them in the last.

open as a page

How do user destinations work in Spring STOMP messaging (@SendToUser, /user prefix, convertAndSendToUser)?

level: middleimportance: must knowfreq 65%

basics

~20 s

User destinations let you send to one specific user, not everyone. The client subscribes to /user/queue/... and the server sends via convertAndSendToUser(user, "/queue/...", payload) or returns from a @MessageMapping method annotated @SendToUser. Spring maps it to a session-unique queue.

open as a page

How do you secure STOMP messages with per-destination authorization in Spring Security (modern AuthorizationManager approach)?

level: seniorimportance: must knowfreq 50%

basics

~10 s

Add Spring Security's WebSocket message security. In Spring Security 6 you annotate a config with @EnableWebSocketSecurity and define an AuthorizationManager<Message<?>> using MessageMatcherDelegatingAuthorizationManager.builder(), matching by destination (simpDestMatchers / simpSubscribeDestMatchers) and requiring roles/authentication, ending with anyMessage().denyAll().

open as a page

What does calling withSockJS() on a STOMP endpoint registration do, and why would you use it?

level: juniorimportance: should knowfreq 55%

basics

~20 s

withSockJS() enables SockJS fallback on a STOMP endpoint. If the browser or a proxy can't use a raw WebSocket, SockJS emulates one over HTTP transports (XHR streaming, XHR polling), so the app still works everywhere.

open as a page

How do you push messages to WebSocket clients from arbitrary server code (outside a @MessageMapping handler)?

level: middleimportance: should knowfreq 58%

basics

~20 s

Inject SimpMessagingTemplate and call convertAndSend(destination, payload) to broadcast to a topic, or convertAndSendToUser(user, destination, payload) to reach one user. This works from any bean — a service, scheduler, or event listener — not just message handlers.

open as a page

Compare the in-memory simple broker with the StompBrokerRelay (RabbitMQ/ActiveMQ). When and why switch?

level: seniorimportance: should knowfreq 55%

basics

~20 s

The simple broker keeps subscriptions and does fan-out in the app's memory — easy but single-instance and non-durable. The StompBrokerRelay forwards STOMP frames to a real broker (RabbitMQ/ActiveMQ), so multiple app instances share subscriptions and you get scaling and reliability.

open as a page

Explain the WebSocket HTTP upgrade handshake and how you hook into it in Spring (HandshakeInterceptor).

level: seniorimportance: should knowfreq 40%

basics

~20 s

A WebSocket connection starts as an HTTP GET with Upgrade: websocket and Connection: Upgrade headers plus a Sec-WebSocket-Key. The server replies 101 Switching Protocols, after which the same TCP socket carries WebSocket frames. In Spring you hook this with a HandshakeInterceptor to inspect/authorize the request and pass attributes into the session.

open as a page

Is WebSocketSession.sendMessage thread-safe, and how do you safely send to a session from multiple threads?

level: seniorimportance: should knowfreq 35%

basics

~10 s

No. Concurrent sendMessage calls on the same WebSocketSession are not safe and can corrupt the frame stream or throw. Wrap the session in a ConcurrentWebSocketSessionDecorator, which serializes sends and buffers them with size/time limits.

open as a page

How does Spring resolve the Principal used for user-destination routing over a STOMP/WebSocket connection, and how would you set it manually?

level: seniorimportance: should knowfreq 45%

basics

~20 s

The Principal comes from the WebSocket session — usually the authenticated user from the HTTP handshake (Spring Security). If auth happens inside STOMP instead, a ChannelInterceptor on the inbound channel reads credentials from the CONNECT frame and sets the user via StompHeaderAccessor.setUser(principal).

open as a page

When would you choose the raw low-level WebSocket API over STOMP, and what do you give up?

level: principalimportance: should knowfreq 30%

basics

~20 s

Use the raw API when you need a custom or minimal protocol, tight control over frames, or interop with a non-STOMP client. You give up STOMP's built-in destinations, pub/sub broadcast, subscriptions, @MessageMapping routing, acks, and broker integration — you'd have to build session tracking and fan-out yourself.

open as a page

convertAndSendToUser works on a single instance but silently fails in a multi-node cluster. Why, and how do you fix it?

level: principalimportance: should knowfreq 25%

basics

~20 s

convertAndSendToUser only knows sessions on the local node's SimpUserRegistry. If the target user is connected to a different node, the message goes nowhere. Fix it by using an external STOMP broker relay and enabling cross-node user registry and user-destination broadcast.

open as a page

How would you architect and secure a horizontally-scaled STOMP-over-WebSocket system, and what failure modes matter?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

Use an external STOMP broker relay so all app instances share subscriptions, authenticate and authorize at both the handshake and per-message level, plan for sticky/routable connections at the load balancer, and design for broker and relay failures with reconnection and backpressure handling.

open as a page