Explain application vs broker destination prefixes and how @MessageMapping / @SendTo route messages.
answer
- /app -> @MessageMapping controllers
- /topic /queue -> broker fan-out
- prefix stripped: /app/hello -> @MessageMapping("/hello")
- default return dest = /topic/<mapping>
- @SendToUser = per-principal /user/...
basics
~20 sApplication 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.
solid answer
~40 sSpring routes a STOMP message by matching its destination against the configured prefixes. `setApplicationDestinationPrefixes("/app")` means a client SEND to `/app/hello` is delivered to your `@MessageMapping("/hello")` method — these are messages you want server-side logic to handle. `enableSimpleBroker("/topic", "/queue")` means destinations starting with `/topic` or `/queue` are broker-managed: clients SUBSCRIBE there, and messages sent there are fanned out to subscribers without hitting a controller. Inside a `@MessageMapping` method, the return value is, by default, published to `/topic/<mappedName>`; `@SendTo("/topic/greetings")` overrides that target destination. Note the prefix asymmetry: the client sends to `/app/hello` but the controller mapping is just `/hello` — the application prefix is stripped before matching. This split keeps 'business logic' destinations and 'broadcast' destinations cleanly separated.
code
java · 18 lines@Controller
public class ChatController {
// client SENDs to /app/chat/{roomId}
@MessageMapping("/chat/{roomId}")
@SendTo("/topic/rooms/{roomId}") // broadcast to that room's subscribers
public ChatEvent onMessage(@DestinationVariable String roomId,
@Payload ChatMessage in,
Principal principal) {
return new ChatEvent(principal.getName(), roomId, in.text());
}
@MessageExceptionHandler
@SendToUser("/queue/errors") // private error reply to sender
public String onError(Exception e) {
return "error: " + e.getMessage();
}
}go deeper
Know /app = handled by controllers, /topic = broadcast to subscribers.
Explain prefix stripping, default return destination, and @SendTo vs @SendToUser.
Add @DestinationVariable, @SubscribeMapping request/reply, and MessageConverter serialization details.
Design a destination namespace convention (topics vs queues vs user destinations) that scales across an external broker and multiple app instances.
### Two kinds of destinations Every STOMP frame carries a `destination` header (e.g. `/app/chat`, `/topic/room1`). Spring decides what to do with a message by matching that destination's **prefix**: **Application destination prefixes** — configured via `registry.setApplicationDestinationPrefixes("/app")`. Messages whose destination starts with `/app` are routed to the *annotation method handler*, i.e. your `@Controller` beans' `@MessageMapping` methods. These are destinations where you want to run server code (validate, persist, transform) before anything is broadcast. **Broker destination prefixes** — configured via `registry.enableSimpleBroker("/topic", "/queue")` (or `enableStompBrokerRelay(...)`). Messages/subscriptions whose destination starts with these prefixes are handled directly by the **broker**. Clients `SUBSCRIBE` to `/topic/...` to receive broadcasts; anything published there is fanned out. By convention `/topic` = one-to-many (pub/sub) and `/queue` = one-to-one (point-to-point), though the simple broker treats both similarly. ### The prefix-stripping detail (common gotcha) When a client sends to `/app/hello`, the application prefix `/app` is **stripped** before matching, so the controller mapping is `@MessageMapping("/hello")`, NOT `@MessageMapping("/app/hello")`. Getting this wrong is the classic 'my handler never fires' bug. ### @MessageMapping and @SendTo ```java @Controller public class GreetingController { @MessageMapping("/hello") // matches client SEND to /app/hello @SendTo("/topic/greetings") // return value published here public Greeting greet(HelloMessage msg) { return new Greeting("Hello, " + msg.getName()); } } ``` - `@MessageMapping` maps an inbound application destination to a handler method. - The method **return value** is serialized (via a `MessageConverter`, JSON by default with Jackson on the classpath) and sent to a broker destination. - **Default destination** without `@SendTo`: `/topic/<mapping>` (e.g. `/topic/hello`). `@SendTo` overrides this — you can even list multiple destinations. - `@SendToUser` sends to a user-specific destination (`/user/{username}/...`) resolved per authenticated principal — used for private replies. ### Method arguments Handler methods can accept: the payload (`@Payload`, or a plain object — default), `@Header`/`@Headers`, `@DestinationVariable` for templated destinations like `@MessageMapping("/chat/{roomId}")`, the `Principal`, and `Message<?>` / `SimpMessageHeaderAccessor`. ### Related annotations - `@SubscribeMapping` — handles a SUBSCRIBE frame and returns a one-time reply directly to that subscriber (request/reply style), bypassing the broker. - `@MessageExceptionHandler` — handles exceptions thrown in messaging methods, optionally with `@SendToUser` for error replies. ### Gotchas - Forgetting that `/app` is stripped in the mapping. - Sending to a broker prefix you didn't enable — the message is silently dropped/unroutable. - Assuming `@SendTo` goes back only to the sender — it goes to the destination, i.e. ALL subscribers. Use `@SendToUser` / `SimpMessagingTemplate.convertAndSendToUser` for private replies.
- A client sends to /app/hello but the @MessageMapping value is /app/hello and nothing fires. Why?The application prefix /app is stripped before matching, so the mapping must be @MessageMapping("/hello"). Prefixing the mapping with /app makes it never match.
- Where does a @MessageMapping return value go if there's no @SendTo?To the default broker destination /topic/<mapping>, e.g. a method mapped to /hello broadcasts to /topic/hello.
- How do you reply privately to just the sending user instead of broadcasting?Use @SendToUser (or SimpMessagingTemplate.convertAndSendToUser) which targets /user/{principal}/... resolved to that user's session.
saying these in an interview costs you the question
- Thinking @SendTo replies only to the sender rather than all subscribers of the destination
- Writing @MessageMapping("/app/hello") and expecting /app/hello sends to match
- Believing /topic messages are routed to controllers
- Confusing the SUBSCRIBE destination with the SEND destination