Master STOMP Messaging in Spring Boot 3: From Basics to Real‑World Examples
This tutorial explains the STOMP protocol, its frame structure, and how Spring Boot 3 integrates STOMP over WebSocket, providing step‑by‑step code examples for enabling STOMP, sending and receiving messages, handling headers, asynchronous processing, exception handling, and simple publish‑subscribe without a controller.
Environment: SpringBoot 3.2.7
1. Introduction
STOMP (Simple Text Oriented Messaging Protocol) was originally created for scripting languages such as Ruby, Python, and Perl to connect to enterprise message brokers. It solves a minimal subset of common messaging patterns and can run over any reliable bidirectional stream protocol like TCP or WebSocket. Although STOMP is text‑based, the payload can be either text or binary.
STOMP is a frame‑based protocol whose frame model is inspired by HTTP. The basic frame structure is:
<code>COMMAND
header1:value1
header2:value2
Body^@</code>Clients use SEND or SUBSCRIBE commands to publish or subscribe to messages, using the destination header to describe the content and receiver, thus implementing a simple publish‑subscribe mechanism.
When Spring's STOMP support is used, the Spring WebSocket application acts as a STOMP broker for clients. Messages are routed either to @Controller handler methods or to an in‑memory broker that tracks subscriptions and broadcasts to subscribed users.
Subscribe message structure
<code>SUBSCRIBE
id:sub-1
destination:/topic/xxxooo
^@</code>Send message structure
<code>SEND
destination:/app/trade
content-type:application/json
content-length:44
{"title": "title","author": "xxxooo","content": "content"}^@</code>Server‑to‑client message
<code>MESSAGE
message-id:4rei3wyj-3
subscription:sub-1721185742991-939
destination:/topic/news
{"title": "title","author": "xxxooo","content": "content"}^@</code>Key advantages of STOMP:
No need to invent a custom transport protocol or message format.
STOMP clients, including the Java client in the Spring framework, are readily available.
Message brokers such as RabbitMQ or ActiveMQ can be optionally used to manage subscriptions and broadcast messages.
Application logic can be organized in any number of @Controller instances, and messages are routed based on STOMP destination headers rather than raw WebSocket handling.
Spring Security can protect messages based on STOMP destinations and message types.
After enabling a message broker in Spring, the components work as shown in the diagram below.
/topic routes messages to the broker.
2. Practical Example
2.1 Enable STOMP
The spring‑messaging and spring‑websocket modules provide STOMP support on top of WebSocket. Adding the following configuration enables a STOMP endpoint with SockJS fallback.
<code>@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// /handshake is the HTTP URL that WebSocket (or SockJS) clients use for the handshake
registry.addEndpoint("/handshake").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
// Messages with destination prefix "/app" are routed to @MessageMapping methods
config.setApplicationDestinationPrefixes("/app");
// Enable a simple in‑memory broker for "/topic" and "/queue"
config.enableSimpleBroker("/topic", "/queue");
}
}
</code>Client‑side resources include the sockjs-client library and the webstomp-client library.
2.2 Basic client example
<code>let stompClient = null;
window.onload = () => {
let socket = new SockJS("/handshake");
stompClient = webstomp.over(socket, {debug: true});
// Connect
stompClient.connect({}, function(frame) {
if (frame.command == 'CONNECTED') {
console.log("连接成功");
}
});
};
// Send a message
function sendGreeting() {
stompClient.send("/app/greeting", `你好, 你好, 我是客户端}`);
}
</code>Server‑side controller handling the greeting:
<code>@Controller
public class StompMessageController {
@MessageMapping("/greeting")
public String handle(String greeting) {
System.out.printf("接收到消息: %s%n", greeting);
return "我是服务端: " + greeting;
}
}
</code>To receive messages, the client subscribes to the destination:
<code>stompClient.subscribe('/topic/greeting', (data) => {
console.log("接收到来自服务端消息,", data.body, data);
});
</code>2.3 Setting and getting headers
<code>stompClient.send("/app/greeting", `你好, 我是客户端`, {'x-version':'1.0.1'});
</code>Server method can retrieve the custom header with @Header:
<code>@MessageMapping("/greeting")
public String handle(String greeting, @Header("x-version") String version) {
// process greeting and version
return greeting + " version:" + version;
}
</code>2.4 Sending and receiving object messages
<code>stompClient.send("/app/payload", JSON.stringify({title:'高考喜讯', author:'Admin', body:'XXX高考第一名'}));
</code>Server side receives the JSON as a POJO using @Payload:
<code>@MessageMapping("/payload")
public Message payload(@Payload Message message) {
System.out.printf("接收到消息对象@Payload: %s%n", message);
return message;
}
</code>2.5 Asynchronous messages
Handler methods may return ListenableFuture , CompletableFuture or CompletionStage for async processing.
<code>@MessageMapping("/payload")
public CompletableFuture<Message> payload(@Payload Message message) {
System.out.printf("接收到消息对象@Payload: %s%n", message);
return CompletableFuture.supplyAsync(() -> {
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {}
return message;
});
}
</code>The client receives the response after the simulated 3‑second delay.
2.6 Message exception handling
<code>@MessageExceptionHandler({Exception.class})
public String error(Exception e) {
return e.getMessage();
}
</code>Throwing an exception inside a @MessageMapping method (e.g., division by zero) triggers the above handler and the error is delivered to the client.
2.7 Publish‑subscribe without a controller
Publishing from index.html :
<code>stompClient.send("/topic/news", `最新消息XXXOOO阵亡`);
</code>Subscribing from other.html :
<code>stompClient.subscribe('/topic/news', (data) => {
console.log("接收到新闻", data.body);
});
</code>No additional server code is required for this simple pub‑sub scenario.
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.