为了账号安全,请及时绑定邮箱和手机立即绑定

基于Spring Boot和WebSocket构建实时通知系统详解

标签:
Java SpringBoot

实时通知功能是许多现代应用程序的关键功能,增强了用户的参与度并提高了响应速度。Spring Boot凭借其强大的生态系统,使实现实时通知功能变得简单而高效。

在这篇博客里,我们将引导你使用Spring Boot和WebSocket构建一个实时通知系统,并展示怎么无缝集成这一重要功能。

实时通知是什么呢?

实时通知功能会立刻把更新推送到用户,省去了刷新和反复检查的麻烦。

比如说:

  • 聊天应用比如 Slack 或者 WhatsApp
  • 股票市场的最新消息
  • 仪表板上的提醒
先决条件

在开始之前,请确认你准备好以下几点:

  • 了解 Spring Boot 和 Java 的基础知识
  • 熟悉 WebSocket 协议
  • 熟悉前端技术,例如 JavaScript 和 HTML
搭建Spring Boot应用程序
1. 添加依赖

首先,创建一个新的,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 流或长轮询。
3. 创建一个消息控制器

创建一个用于处理传入的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 的订阅者。
示例流程示例
  1. 客户发送了一条消息:
  • 一个 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 实例。
  1. **踩踏.过**(即Stomp.over):

    将 SockJS 实例打包到一个 STOMP 客户端中,以提供更高级的消息处理能力。

  2. **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: 运行应用
  1. 启动 Spring Boot 应用。
  2. 在浏览器里打开 HTML 文件。
  3. 在输入框里输入一条消息并点击 发送 按钮。消息将实时显示在通知区域。
多个客户端测试
  1. 在多个浏览器标签中打开HTML页面。
  2. 当一个标签发送消息时,其他标签会立即收到通知,因为所有客户端都订阅了同一个消息服务器。

这就是结果

这是实时通知模板的页面链接

在这里你可以检查客户端日志。

如果你仔细看,每条消息都有一个唯一的消息ID,并且这个ID对于每个用户都是不同的。现在,让我们将这个概念应用到实际中,比如在WhatsApp群组中。

假设我创建了一个名为javatechie-KT的群组,并添加了几位成员。每当我在该群组中发布消息时,我们都能看到这条消息,因为所有订阅这个群组的人都能看到。同样地,如果群组中的任何成员发送消息,我们都能立刻看到这条消息,因为消息会被广播到该群组指定的单一订阅者,并且我们也都订阅了这个订阅者。

我希望这个类比能帮助你更清楚地理解这样的系统是如何运作的,并扩展您的理解来实现这样的实时通知系统 GitHub 源码

好了各位!希望这篇文章对你们有所帮助!如果你们想深入了解并随时获取我的最新见解和文章,欢迎并通过LinkedIn联系我,别忘了关注我的博客哦。

📢 这里有激动人心的消息!

如果你想成为Spring Boot微服务的高手,并且掌握全套DevOps技能,不妨来看看我的独家课程!🚀

🔹 DevOps入门(面向开发人员)
👉 立即加入
优惠码:NEW24

🔹 Spring Boot 微服务高级课程 🚀
👉 查看课程
优惠码:SPRING50

如果你觉得这些有用,就和朋友们分享吧!一起努力吧! 💻🔥

谢谢 :)

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消