SpringBoot+WebSocket实现实时消息推送系统(在线聊天/通知)

 更新时间:2026年06月30日 08:55:34   作者:张老师技术栈  
本文详细介绍了如何使用SpringBoot整合WebSocket实现服务端主动推送消息,覆盖了从零搭建实时消息推送系统的核心步骤;包括引入依赖、配置WebSocket、编写WebSocketServer以及业务代码调用推送等关键环节,需要的朋友可以参考下

WebSocket 是实现服务端主动推送消息的常用技术,适用于在线聊天、系统通知、实时数据展示等场景。本文从零搭建一个 SpringBoot + WebSocket 的实时消息推送系统。

一、WebSocket 是什么

传统 HTTP 通信只能由客户端发起请求,服务端被动响应。WebSocket 建立连接后,服务端可以主动向客户端推送消息

对比HTTPWebSocket
通信方向客户端→服务端双向通信
连接方式每次请求新建连接建立连接后保持
实时性需要轮询即时推送
适用场景普通 API聊天、通知、实时数据

二、SpringBoot 整合 WebSocket

1. 引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

2. 配置 WebSocket

@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

三、核心:WebSocket 服务端

@Component
@ServerEndpoint("/ws/{userId}")
@Slf4j
public class WebSocketServer {

    /** 存放每个用户的 WebSocket 连接 */
    private static Map<String, Session> sessionMap = new ConcurrentHashMap<>();

    /**
     * 连接建立成功
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        sessionMap.put(userId, session);
        log.info("用户 {} 已连接,当前在线人数: {}", userId, sessionMap.size());
    }

    /**
     * 连接关闭
     */
    @OnClose
    public void onClose(@PathParam("userId") String userId) {
        sessionMap.remove(userId);
        log.info("用户 {} 已断开,当前在线人数: {}", userId, sessionMap.size());
    }

    /**
     * 收到客户端消息
     */
    @OnMessage
    public void onMessage(String message, @PathParam("userId") String userId) {
        log.info("收到用户 {} 的消息: {}", userId, message);
    }

    /**
     * 发生错误
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("WebSocket 错误", error);
    }

    /**
     * 向指定用户推送消息
     */
    public static void sendToUser(String userId, String message) {
        Session session = sessionMap.get(userId);
        if (session != null && session.isOpen()) {
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                log.error("推送消息失败", e);
            }
        }
    }

    /**
     * 向所有用户广播消息
     */
    public static void broadcast(String message) {
        sessionMap.values().forEach(session -> {
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                log.error("广播消息失败", e);
            }
        });
    }

    /**
     * 获取在线用户数
     */
    public static int getOnlineCount() {
        return sessionMap.size();
    }
}

四、服务端主动推送消息

在 Controller 中调用 WebSocket 推送消息:

@RestController
@RequestMapping("/push")
public class PushController {

    /**
     * 推送给指定用户
     */
    @PostMapping("/user/{userId}")
    public ResultVO pushToUser(@PathVariable String userId, @RequestBody String message) {
        WebSocketServer.sendToUser(userId, message);
        return ResultVO.success("推送成功");
    }

    /**
     * 广播给所有用户
     */
    @PostMapping("/broadcast")
    public ResultVO broadcast(@RequestBody String message) {
        WebSocketServer.broadcast(message);
        return ResultVO.success("广播成功");
    }

    /**
     * 获取在线人数
     */
    @GetMapping("/online")
    public ResultVO getOnlineCount() {
        return ResultVO.success(WebSocketServer.getOnlineCount());
    }
}

五、前端页面连接 WebSocket

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>WebSocket 消息推送</title>
</head>
<body>
    <h3>WebSocket 实时消息</h3>
    <div>
        <input type="text" id="userId" placeholder="用户ID" value="user001">
        <button onclick="connect()">连接</button>
        <button onclick="disconnect()">断开</button>
    </div>
    <div style="margin-top: 10px;">
        <div id="messages" style="border:1px solid #ccc; height:300px; overflow-y:scroll; padding:10px;"></div>
    </div>
    <script>
        let websocket = null;
        function connect() {
            const userId = document.getElementById('userId').value;
            websocket = new WebSocket('ws://localhost:8080/ws/' + userId);
            websocket.onopen = function() {
                appendMessage('✅ 连接成功,用户ID: ' + userId);
            };
            websocket.onmessage = function(event) {
                appendMessage('📩 收到消息: ' + event.data);
            };
            websocket.onclose = function() {
                appendMessage('❌ 连接已断开');
            };
            websocket.onerror = function(error) {
                appendMessage('⚠️ 连接出错');
            };
        }
        function disconnect() {
            if (websocket) {
                websocket.close();
            }
        }
        function appendMessage(msg) {
            const div = document.getElementById('messages');
            div.innerHTML += '<div>' + msg + '</div>';
            div.scrollTop = div.scrollHeight;
        }
    </script>
</body>
</html>

六、实战:系统通知推送

实际项目中,在业务代码中调用推送:

@Service
public class OrderService {

    public void createOrder(Order order) {
        // 1. 保存订单
        orderMapper.insert(order);

        // 2. 推送通知给用户
        JSONObject msg = new JSONObject();
        msg.put("type", "order_notify");
        msg.put("orderId", order.getId());
        msg.put("message", "您的订单已创建成功");

        WebSocketServer.sendToUser(
            order.getUserId().toString(),
            msg.toJSONString()
        );
    }
}

客户端收到消息后,根据 type 字段做相应处理:

websocket.onmessage = function(event) {
    const data = JSON.parse(event.data);
    if (data.type === 'order_notify') {
        showNotification('📦 ' + data.message);
    } else if (data.type === 'system_notify') {
        showNotification('🔔 ' + data.message);
    }
};

七、常见问题

1. WebSocket 连接被拦截

如果项目中有拦截器,需要放行 WebSocket 请求:

registry.addInterceptor(loginInterceptor)
    .addPathPatterns("/**")
    .excludePathPatterns("/ws/**");  // 放行 WebSocket

2. 跨域问题

@Bean
public ServerEndpointExporter serverEndpointExporter() {
    return new ServerEndpointExporter();
}

// WebSocket 原生不支持跨域配置
// SpringBoot 方式需要在配置中添加
@Bean
public WebSocketConfigurer webSocketConfigurer() {
    return registry -> registry.addHandler(webSocketHandler(), "/ws/{userId}")
            .setAllowedOrigins("*");
}

3. 心跳保活

WebSocket 长时间空闲可能被断开,定期发送心跳包:

// 客户端每 30 秒发送心跳
setInterval(() => {
    if (websocket && websocket.readyState === WebSocket.OPEN) {
        websocket.send('ping');
    }
}, 30000);

总结

WebSocket 是实时消息推送的首选方案。SpringBoot 整合 WebSocket 非常简单,三步走:

  1. 引入依赖 + 配置
  2. 编写 WebSocketServer —— @OnOpen、@OnClose、@OnMessage
  3. 业务代码调用推送 —— 面向指定用户或广播

到此这篇关于SpringBoot+WebSocket实现实时消息推送系统(在线聊天/通知)的文章就介绍到这了,更多相关SpringBoot WebSocket实时消息推送内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • selenium高效应对Web页面元素刷新的实例讲解

    selenium高效应对Web页面元素刷新的实例讲解

    今天小编就为大家分享一篇selenium高效应对Web页面元素刷新的实例讲解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-05-05
  • 简单分析java中CMS回收器

    简单分析java中CMS回收器

    在本篇文章里我们给大家分享了关于java中CMS回收器的相关知识点内容,有需要的朋友们可以跟着学习下。
    2018-10-10
  • java使用poi读取doc和docx文件的实现示例

    java使用poi读取doc和docx文件的实现示例

    这篇文章主要介绍了java使用poi读取doc和docx文件的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03
  • Spring执行流程和Bean的生命周期详解

    Spring执行流程和Bean的生命周期详解

    这篇文章主要介绍了Spring执行流程和Bean的生命周期详解,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-06-06
  • 深入理解Java的Spring框架中的IOC容器

    深入理解Java的Spring框架中的IOC容器

    IOC(Inversion of Control,控制反转)是Spring框架的核心,负责控制对象的生命周期与关系,接下来就让我们跟随文章来深入理解Java的Spring框架中的IOC容器:
    2016-07-07
  • Java如何将String转换成json对象或json数组

    Java如何将String转换成json对象或json数组

    这篇文章主要介绍了Java如何将String转换成json对象或json数组,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • Springboot中如何使用Redisson实现分布式锁浅析

    Springboot中如何使用Redisson实现分布式锁浅析

    redisson是redis的java客户端程序,国内外很多公司都有在用,下面这篇文章主要给大家介绍了关于Springboot中如何使用Redisson实现分布式锁的相关资料,需要的朋友可以参考下
    2021-10-10
  • java如何消除太多的if else判断示例代码

    java如何消除太多的if else判断示例代码

    这篇文章主要介绍了java如何消除太多的if else判断,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-05-05
  • 单例模式垃圾回收_动力节点Java学院整理

    单例模式垃圾回收_动力节点Java学院整理

    这篇文章主要为大家详细介绍了单例模式垃圾回收的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-08-08
  • Java binLog日志监听方式

    Java binLog日志监听方式

    文章介绍了如何在Windows下开启MySQL的binLog日志,并提供了一个Java代码示例,演示如何监听指定的表并进行处理逻辑
    2024-11-11

最新评论