在现代 Web 应用中,实时通信越来越重要。传统的 HTTP 请求-响应模型已经无法满足某些实时性要求较高的应用场景,如在线聊天、通知推送、实时数据更新等。为了解决这个问题,我们可以利用 WebSocket 技术,而 STOMP 协议则可以进一步简化 WebSocket 的使用。本文将通过 Spring Boot 3.3 和 STOMP 协议的结合,构建一个高效的实时数据通信系统。
什么是 STOMP 协议?
STOMP(Streaming Text Oriented Messaging Protocol)是一种简单的文本消息协议,类似于 HTTP。它定义了如何通过网络协议进行消息的发送和接收。在 WebSocket 的基础上使用 STOMP 协议可以极大地简化消息的处理,使得消息系统更加结构化、可扩展且易于管理。
STOMP 协议的优势
-
简单易用:STOMP 使用文本格式,非常容易解析和调试。
-
消息订阅/发布:通过
SUBSCRIBE
和UNSUBSCRIBE
命令可以轻松实现消息的订阅和取消订阅。 -
兼容性好:STOMP 支持与现有的消息代理系统(如 ActiveMQ、RabbitMQ)配合使用。
-
跨语言支持:STOMP 不依赖于特定语言,允许不同语言的客户端通过 STOMP 通信。
项目配置
在本文的示例中,我们将使用 Spring Boot 3.3 和 STOMP 实现一个简单的实时通信应用,包括消息的发送、异步处理和发布/订阅机制。前端页面使用 jQuery
和 Bootstrap
,通过 HTTP 方式直接引用。
运行效果:
在现代 Web 应用中,实时通信越来越重要。传统的 HTTP 请求-响应模型已经无法满足某些实时性要求较高的应用场景,如在线聊天、通知推送、实时数据更新等。为了解决这个问题,我们可以利用 WebSocket 技术,而 STOMP 协议则可以进一步简化 WebSocket 的使用。本文将通过 Spring Boot 3.3 和 STOMP 协议的结合,构建一个高效的实时数据通信系统。
什么是 STOMP 协议?
STOMP(Streaming Text Oriented Messaging Protocol)是一种简单的文本消息协议,类似于 HTTP。它定义了如何通过网络协议进行消息的发送和接收。在 WebSocket 的基础上使用 STOMP 协议可以极大地简化消息的处理,使得消息系统更加结构化、可扩展且易于管理。
STOMP 协议的优势
-
简单易用:STOMP 使用文本格式,非常容易解析和调试。
-
消息订阅/发布:通过
SUBSCRIBE
和UNSUBSCRIBE
命令可以轻松实现消息的订阅和取消订阅。 -
兼容性好:STOMP 支持与现有的消息代理系统(如 ActiveMQ、RabbitMQ)配合使用。
-
跨语言支持:STOMP 不依赖于特定语言,允许不同语言的客户端通过 STOMP 通信。
项目配置
在本文的示例中,我们将使用 Spring Boot 3.3 和 STOMP 实现一个简单的实时通信应用,包括消息的发送、异步处理和发布/订阅机制。前端页面使用 jQuery
和 Bootstrap
,通过 HTTP 方式直接引用。
运行效果:
若想获取项目完整代码以及其他文章的项目源码,且在代码编写时遇到问题需要咨询交流,欢迎加入下方的知识星球。
项目依赖配置 (pom.xml
)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.icoderoad</groupId>
<artifactId>stomp_demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>stomp_demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<!-- Spring Boot Starter WebSocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Thymeleaf for HTML templates -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置文件 application.yml
server:
port: 8080
app:
websocketEndpoint: /websocket-endpoint
simpleBrokerDestinations:
- /topic
- /queue
applicationDestinationPrefix: /app
属性配置类 (AppProperties
)
我们将 WebSocket 相关的配置信息放入一个属性类 AppProperties
,这样可以更灵活地管理这些参数。
package com.icoderoad.stompdemo.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import lombok.Data;
@Data
@Configuration
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String websocketEndpoint;
private String[] simpleBrokerDestinations;
private String applicationDestinationPrefix;
}
WebSocket 配置类 (WebSocketConfig
)
我们使用 WebSocketMessageBrokerConfigurer
来配置 WebSocket。AppProperties
中的配置将通过 @Autowired
自动注入到 WebSocketConfig
中。
package com.icoderoad.stompdemo.config;
import org.springframework.beans.factory.annotation.Autowired;
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 {
@Autowired
private AppProperties appProperties;
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker(appProperties.getSimpleBrokerDestinations());
config.setApplicationDestinationPrefixes(appProperties.getApplicationDestinationPrefix());
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint(appProperties.getWebsocketEndpoint()).withSockJS();
}
}
Message
类
package com.icoderoad.stompdemo.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Message {
private String content;
}
消息处理控制器 (MessageController
)
在这里,我们定义了不同的消息处理方法,包括异步处理和发布/订阅模式。
package com.icoderoad.stompdemo.config;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureTask;
import com.icoderoad.stompdemo.entity.Message;
@Controller
public class MessageController {
// 简单的消息发送,接收 JSON 对象并返回
@MessageMapping("/send")
@SendTo("/topic/messages")
public Message send(Message message) {
return message; // 原样返回消息
}
// 异步消息处理
@MessageMapping("/asyncMessage")
@SendTo("/topic/asyncMessages")
public ListenableFuture<Message> asyncMessage(Message message) {
ListenableFutureTask<Message> futureTask = new ListenableFutureTask<>(() -> {
// 模拟一些处理
Thread.sleep(2000);
return message;
});
new Thread(futureTask).start();
return futureTask;
}
// 发布/订阅模式
@MessageMapping("/broadcast")
@SendTo("/topic/broadcasts")
public String broadcast(String content) {
return "广播消息: " + content;
}
// 处理带异常的消息
@MessageMapping("/errorHandling")
@SendTo("/queue/errors")
public Message handleError(Message message) throws Exception {
if ("error".equalsIgnoreCase(message.getContent())) {
throw new Exception("消息包含错误内容");
}
return message;
}
}
视图控制类
package com.icoderoad.stompdemo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class IndexController {
@GetMapping("/")
public String index() {
return "index";
}
}
前端页面 (index.html
)
前端页面使用 Bootstrap
和 jQuery
进行样式和交互,并通过 HTTP 方式直接引入外部资源。
在 src/main/resources/templates
目录下创建 index.html
文件:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>STOMP 实时通信示例</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1.5.1/dist/sockjs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/stompjs@2.3.3/lib/stomp.min.js"></script>
</head>
<body>
<div class="container">
<h1 class="text-center">STOMP 实时通信示例</h1>
<!-- 输入消息框 -->
<div class="form-group">
<label for="messageInput">消息内容:</label>
<input type="text" id="messageInput" class="form-control" placeholder="输入消息">
</div>
<!-- 发送简单消息按钮 -->
<button id="sendButton" class="btn btn-primary mt-2">发送消息</button>
<!-- 发送异步消息按钮 -->
<button id="sendAsyncButton" class="btn btn-success mt-2">发送异步消息</button>
<!-- 发送发布订阅消息按钮 -->
<button id="broadcastButton" class="btn btn-warning mt-2">发送广播消息</button>
<!-- 显示接收到的消息 -->
<h3 class="text-center mt-5">接收到的消息:</h3>
<div id="messageContainer" class="border p-3"></div>
</div>
<script type="text/javascript">
var stompClient = null;
// 建立 WebSocket 连接
function connect() {
var socket = new SockJS('/websocket-endpoint');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
console.log('已连接: ' + frame);
// 订阅消息主题
stompClient.subscribe('/topic/messages', function(message) {
showMessage(JSON.parse(message.body).content);
});
// 订阅异步消息主题
stompClient.subscribe('/topic/asyncMessages', function(message) {
showMessage("异步消息: " + JSON.parse(message.body).content);
});
// 订阅广播消息
stompClient.subscribe('/topic/broadcasts', function(message) {
showMessage("广播消息: " + message.body);
});
});
}
// 显示消息
function showMessage(message) {
$("#messageContainer").append("<p>" + message + "</p>");
}
// 发送消息
$("#sendButton").click(function() {
var message = {content: $("#messageInput").val()};
stompClient.send("/app/send", {}, JSON.stringify(message));
});
// 发送异步消息
$("#sendAsyncButton").click(function() {
var message = {content: $("#messageInput").val()};
stompClient.send("/app/asyncMessage", {}, JSON.stringify(message));
});
// 发送广播消息
$("#broadcastButton").click(function() {
stompClient.send("/app/broadcast", {}, $("#messageInput").val());
});
// 连接 WebSocket
connect();
</script>
</body>
</html>
总结
本文详细介绍了如何在Spring Boot 3.3中结合STOMP协议实现实时数据通信。我们通过配置WebSocket支持、创建消息控制器、构建前端界面,并结合@ConfigurationProperties
进行配置管理,实现了一个功能完备的实时消息系统。STOMP协议提供的简化消息传输和高效通信的优势,使得它成为构建实时Web应用的理想选择。希望通过本文,大家能掌握如何在 Spring Boot3.3 项目中利用 STOMP 协议简化实时通信的实现。