基于Spring Boot和WebSocket构建实时通知系统详解
实时通知功能是许多现代应用程序的关键功能,增强了用户的参与度并提高了响应速度。Spring Boot凭借其强大的生态系统,使实现实时通知功能变得简单而高效。
在这篇博客里,我们将引导你使用Spring Boot和WebSocket构建一个实时通知系统,并展示怎么无缝集成这一重要功能。
实时通知是什么呢?
实时通知功能会立刻把更新推送到用户,省去了刷新和反复检查的麻烦。
比如说:
- 聊天应用比如 Slack 或者 WhatsApp
- 股票市场的最新消息
- 仪表板上的提醒
在开始之前,请确认你准备好以下几点:
- 了解 Spring Boot 和 Java 的基础知识
- 熟悉 WebSocket 协议
- 熟悉前端技术,例如 JavaScript 和 HTML
首先,创建一个新的,Spring Boot应用,并将以下依赖项添加到现有的pom.xml
中。
<依赖项>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</依赖项>
2. 启用 WebSocket 功能
通过在主应用类或配置类中加入 @EnableWebSocketMessageBroker
注解来开启 WebSocket 支持功能。
// WebSocketConfig.java
package com.javatechie.config;
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 // 启用WebSocket消息代理功能
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic"); // 设置用于广播消息的前缀
config.setApplicationDestinationPrefixes("/app"); // 客户端向服务器发送消息的前缀
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws") // WebSocket端点配置
.setAllowedOrigins("http://localhost:63342") // 允许前端来源 (localhost:63342) 进行访问
.withSockJS(); // 启用SockJS,以便在WebSocket不支持时提供回退支持
}
}
**@EnableWebSocketMessageBroker**
:启用WebSocket消息处理功能。它支持通过STOMP(简单文本导向消息协议)来处理WebSocket消息。
**enableSimpleBroker("/topic")**
:启用一个简单的内存消息代理,将带有/topic
前缀的消息路由给订阅者。- 例如:发送到
/topic/notifications
的消息将广播给所有订阅此主题的客户端。 **setApplicationDestinationPrefixes("/app")**
:设置从客户端发送到服务器的消息的前缀。- 例如:客户端发送消息到
/app/sendMessage
将触发映射于@MessageMapping("/sendMessage")
的控制器方法。 **addEndpoint("/ws")**
:将/ws
定义为客户端用于建立连接的 WebSocket 端点。- 例如:前端可以连接到
ws://localhost:8080/ws
来启动 WebSocket 会话。 **setAllowedOrigins("http://localhost:63342")**
:限制允许连接的源(前端域名)。这对于 CORS 很重要。- 在这里,源被限制为此特定本地开发环境的域名
http://localhost:63342
,通常用于本地前端开发。 **withSockJS()**
:启用 SockJS,这是一个在浏览器不支持 WebSocket 时提供备选方案的库,比如 HTTP 流或长轮询。- 如果 WebSocket 不可用,它将退回到备选方案,如 HTTP 流或长轮询。
创建一个用于处理传入的WebSocket消息的控制器:
// NotificationController.java
package com.javatechie.controller;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
@Controller
public class NotificationController {
@MessageMapping("/sendMessage") // 匹配 JavaScript 目的地的端点
@SendTo("/topic/notifications") // 广播给订阅这个主题的用户
public String sendMessage(String message) {
System.out.println("消息: " + message); // 调试信息
return message; // 广播消息
}
// 发送的消息
// 发送消息的方法
}
**@MessageMapping("/sendMessage")**
- 将客户端发送到
/app/sendMessage
(在WebSocketConfig
中配置)的消息映射到此方法。 @MessageMapping
的目的地会自动加上/app
前缀。- 示例:
- 当客户端向
/app/sendMessage
发送消息时,此方法就会被调用。
**@SendTo("/topic/通知")**
- 指定响应应广播的主题。
- 所有订阅了
/topic/notifications
的客户端都将收到此方法发送的消息。 - 示例:
- 此方法返回的值将发送给所有订阅了
/topic/notifications
的订阅者。
- 客户发送了一条消息:
- 一个 WebSocket 客户端发送消息到
/app/sendMessage
。 - 调用
sendMessage
方法,传入消息负载。
广播信息
- 该方法记录收到的消息。
- 它返回消息,然后把消息广播给所有订阅了
/topic/notifications
的客户端。
3. 订阅者:收到消息:
- 任何订阅了
/topic/notifications
的客户端都会实时收到广播消息。
希望这个流程对你来说清晰明了,如果我们都同意的话,就一起开发前端客户端应用吧。
4. 构建前端(客户端应用)创建一个包含WebSocket连接功能的HTML文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>实时消息提醒</title>
<script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="https://cdn.jsdelivr.net/npm/sockjs-client/dist/sockjs.min.js"></script>
<script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="https://cdn.jsdelivr.net/npm/stompjs/lib/stomp.min.js"></script>
</head>
<body>
<h1>实时消息提醒</h1>
<div id="notifications"></div>
<input type="text" id="message" placeholder="请输入您的消息">
<button onclick="sendMessage()">发送</button>
<script>
let stompClient = null;
function connect() {
const socket = new SockJS('http://localhost:8080/ws'); // 请更正 WebSocket 地址
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log('记录连接成功: ' + frame); // 记录成功连接
stompClient.subscribe('/topic/notifications', function (message) {
showNotification(message.body); // 处理传入的消息
});
}, function (error) {
console.error('记录 WebSocket 错误信息: ' + error); // 记录 WebSocket 错误
});
}
function sendMessage() {
const message = document.getElementById('message').value;
if (stompClient && stompClient.connected) {
stompClient.send('/app/sendMessage', {}, message);
console.log('消息已发送:', message);
} else {
console.error('WebSocket 连接未建立。');
}
}
function showNotification(message) {
const notifications = document.getElementById('notifications');
const notification = document.createElement('p');
notification.textContent = message;
notifications.appendChild(notification);
}
connect();
</script>
</body>
</html>
让我们来拆解这段客户的代码,了解每一步我们具体在做什么。
连接
功能
function connect() {
const socket = new SockJS('http://localhost:8080/ws'); // 注:此处的WebSocket URL需要根据实际情况进行调整
stompClient = Stomp.over(socket); // 使用SockJS创建的socket初始化STOMP客户端
stompClient.connect({}, function (frame) {
stompClient.subscribe('/topic/notifications', function (message) {
showNotification(message.body); // 显示通知:通知内容为(message.body)
}); // 订阅通知频道, 并在收到消息时执行回调函数
}, function (error) {
console.error('WebSocket连接出错: ' + error); // 控制台日志:WebSocket连接错误
});
}
-
目的:为了与后端服务器建立一个 WebSocket 连接,并订阅
/topic/notifications
话题。**SockJS**
:这是一款用来实现浏览器和服务器之间实时通讯的JavaScript库哦。 - 创建一个连接到
/ws
WebSocket 端点的 SockJS 实例。
-
**踩踏.过**
(即Stomp.over):将 SockJS 实例打包到一个 STOMP 客户端中,以提供更高级的消息处理能力。
**connect()**
:连接到服务器的函数
- 建立STOMP连接。
- 成功处理函数:
- 订阅
/topic/notifications
来接收服务器发送的消息。 showNotification(message.body)
显示接收到的这条消息。- 错误处理函数:
- 如果有连接错误,就在控制台上记录下来。
发送消息:**sendMessage**
function sendMessage() {
const message = document.getElementById('message').value;
if (stompClient && stompClient.connected) {
stompClient.send('/app/sendMessage', {}, message);
console.log('发送了消息:', message);
} else {
console.error('WebSocket连接尚未建立。');
}
}
从 message
字段获取输入值,使用 document.getElementById
方法。然后将消息发送到 /app/sendMessage
路径,触发后端的 NotificationController
。
功能:显示通知
定义一个显示通知的函数,用于在页面上显示消息。
function showNotification(message) {
const 通知元素 = document.getElementById('notifications');
const 通知 = document.createElement('p');
通知.textContent = message;
通知元素.appendChild(通知);
}
显示收到的消息。
我们快速测试一下我们实现的实时通知功能。
5: 运行应用- 启动 Spring Boot 应用。
- 在浏览器里打开 HTML 文件。
- 在输入框里输入一条消息并点击 发送 按钮。消息将实时显示在通知区域。
- 在多个浏览器标签中打开HTML页面。
- 当一个标签发送消息时,其他标签会立即收到通知,因为所有客户端都订阅了同一个消息服务器。
这就是结果
在这里你可以检查客户端日志。
如果你仔细看,每条消息都有一个唯一的消息ID,并且这个ID对于每个用户都是不同的。现在,让我们将这个概念应用到实际中,比如在WhatsApp群组中。
假设我创建了一个名为javatechie-KT
的群组,并添加了几位成员。每当我在该群组中发布消息时,我们都能看到这条消息,因为所有订阅这个群组的人都能看到。同样地,如果群组中的任何成员发送消息,我们都能立刻看到这条消息,因为消息会被广播到该群组指定的单一订阅者,并且我们也都订阅了这个订阅者。
我希望这个类比能帮助你更清楚地理解这样的系统是如何运作的,并扩展您的理解来实现这样的实时通知系统 GitHub 源码
好了各位!希望这篇文章对你们有所帮助!如果你们想深入了解并随时获取我的最新见解和文章,欢迎并通过LinkedIn联系我,别忘了关注我的博客哦。
📢 这里有激动人心的消息!
如果你想成为Spring Boot微服务的高手,并且掌握全套DevOps技能,不妨来看看我的独家课程!🚀
🔹 DevOps入门(面向开发人员)
👉 立即加入
优惠码:NEW24
🔹 Spring Boot 微服务高级课程 🚀
👉 查看课程
优惠码:SPRING50
如果你觉得这些有用,就和朋友们分享吧!一起努力吧! 💻🔥
谢谢 :)
共同学习,写下你的评论
评论加载中...
作者其他优质文章