Spring Boot Starter WebSocket 详细说明文档

一、什么是 spring-boot-starter-websocket

spring-boot-starter-websocket 是 Spring Boot 提供的一个自动配置启动器(Starter),用于快速集成 WebSocket 协议支持。它封装了 Spring Framework 的 WebSocket 功能,包括 STOMP(Simple Text Oriented Messaging Protocol)消息协议支持,使得开发者可以轻松地在 Spring Boot 应用中实现双向实时通信

核心依赖包含:

  • spring-websocket:WebSocket 协议核心支持
  • spring-messaging:消息抽象层,支持 STOMP
  • sockjs:兼容性库,用于在不支持 WebSocket 的浏览器中模拟 WebSocket(如降级为轮询)
  • stomp-websocket:STOMP 协议客户端/服务端实现

一句话总结spring-boot-starter-websocket 让你只需几行配置,就能在 Spring Boot 应用中实现类似微信聊天、实时股票行情、在线协作编辑器、游戏实时状态同步等场景的双向通信功能。


二、WebSocket 与 STOMP 的区别

特性WebSocketSTOMP over WebSocket
协议层级原始二进制/文本协议应用层消息协议(基于 WebSocket)
消息格式自定义标准化格式(SEND, SUBSCRIBE, MESSAGE 等)
开发复杂度高(需自己设计协议)低(有标准命令和消息头)
适用场景高性能实时流(如游戏)企业级应用(聊天、通知、仪表盘)
Spring 支持基础支持完整封装(推荐)

💡 推荐使用 STOMP:在大多数业务系统中,STOMP 更易用、可维护、可扩展,Spring Boot 对其支持最完善。


三、主要功能与应用场景

功能描述实际应用场景
实时消息推送服务端主动向客户端推送消息股票行情、系统通知、订单状态更新
多用户聊天多个客户端之间互相发送消息在线客服、团队协作聊天
用户在线状态实时感知用户上线/下线社交应用、IM系统
实时数据监控实时展示设备/服务器指标监控大屏、IoT 数据看板
协同编辑多人同时编辑同一文档Google Docs 类型应用
游戏实时同步玩家动作同步、状态更新浏览器多人小游戏

四、完整开发示例:实时聊天室系统(含详细中文注释)

✅ 项目结构

src/
├── main/
│   ├── java/
│   │   └── com/example/websocketdemo/
│   │       ├── WebSocketDemoApplication.java        // 启动类
│   │       ├── config/WebSocketConfig.java          // WebSocket 配置
│   │       ├── controller/ChatController.java       // 消息控制器
│   │       └── service/ChatService.java             // 业务服务
│   └── resources/
│       └── static/
│           └── chat.html                            // 前端聊天页面

1. Maven 依赖(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 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>websocket-demo</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.0</version>
        <relativePath/>
    </parent>

    <properties>
        <java.version>17</java.version>
    </properties>

    <dependencies>
        <!-- 引入 WebSocket 启动器,自动配置所有必要组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

        <!-- Web 依赖,提供 REST 接口和静态资源服务 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 可选:热部署开发工具 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <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>

2. WebSocket 配置类(WebSocketConfig.java

package com.example.websocketdemo.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;

/**
 * WebSocket 消息代理配置类
 * 
 * 作用:配置 STOMP 消息代理、端点(Endpoint)、消息路由规则
 * 
 * @EnableWebSocketMessageBroker:启用 WebSocket 消息代理功能
 *   - 自动配置 SockJS、STOMP、消息代理(内存或外部如 Redis)
 *   - 启用 @MessageMapping 和 @SubscribeMapping 注解
 */
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    /**
     * 注册 STOMP 端点(客户端连接入口)
     * 
     * 为什么需要端点?
     *   - 客户端通过此 URL 建立 WebSocket 连接(如:ws://localhost:8080/chat)
     *   - SockJS 会降级为 HTTP 长轮询,兼容老浏览器
     * 
     * withSockJS():启用 SockJS 兼容模式(推荐!)
     *   - 当浏览器不支持原生 WebSocket 时,自动降级为 AJAX 轮询
     *   - 保证在 IE9+、移动端等环境也能正常工作
     */
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        // 注册一个名为 "/chat" 的端点
        registry.addEndpoint("/chat")
                .setAllowedOriginPatterns("*")  // 允许所有来源(开发阶段使用,生产环境请限制)
                .withSockJS();                    // 启用 SockJS 兼容模式
    }

    /**
     * 配置消息代理(Message Broker)
     * 
     * 两种代理类型:
     *   1. 内存代理(simple):适用于单机应用,消息存储在 JVM 内存中
     *   2. 外部代理(如 Redis):适用于集群部署,消息跨节点同步
     * 
     * setApplicationDestinationPrefixes:指定客户端发送消息的前缀
     *   - 客户端发送消息时,必须以 "/app" 开头,如:/app/chat
     *   - Spring 会将这些消息路由到 @MessageMapping 注解的方法
     * 
     * setUserDestinationPrefix:指定用户私有消息前缀
     *   - 用于向特定用户发送消息,如:/user/{username}/queue/messages
     *   - 需要结合 @SendToUser 使用
     */
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        // 配置客户端可订阅的主题前缀(广播/群组消息)
        // 客户端使用 subscribe("/topic/public") 订阅公共消息
        registry.enableSimpleBroker("/topic", "/queue");

        // 配置客户端发送消息的目标前缀
        // 客户端发送消息必须以 /app 开头,如:/app/chat
        // Spring 会将这些消息映射到 @MessageMapping("/chat") 的方法
        registry.setApplicationDestinationPrefixes("/app");

        // 配置用户私有消息前缀,用于一对一消息推送
        // 如:/user/张三/queue/messages
        registry.setUserDestinationPrefix("/user");
    }
}

3. 消息控制器(ChatController.java

package com.example.websocketdemo.controller;

import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.annotation.SubscribeMapping;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * STOMP 消息控制器
 * 
 * 作用:接收客户端通过 WebSocket 发送的消息,并广播或定向发送给其他客户端
 * 
 * @Controller:标记为 Spring 控制器,但不返回视图,只处理消息
 * @MessageMapping:映射客户端发送的消息路径(与 setApplicationDestinationPrefixes 配合)
 * @SendTo:将消息广播到指定主题(所有订阅者都能收到)
 * @SubscribeMapping:当客户端订阅某个主题时自动触发(用于初始化数据)
 */
@Controller
public class ChatController {

    /**
     * 接收客户端发送的聊天消息
     * 
     * 客户端发送路径:/app/chat
     * 此方法会自动接收所有发送到 /app/chat 的消息
     * 
     * @Payload:自动将消息体(JSON)绑定为 ChatMessage 对象
     * @SendTo("/topic/public"):将消息广播到 /topic/public 主题
     *   - 所有订阅了 /topic/public 的客户端都会收到这条消息
     * 
     * 注意:消息体必须是 JSON 格式,Spring 自动反序列化为 Java 对象
     */
    @MessageMapping("/chat")
    @SendTo("/topic/public")
    public ChatMessage sendMessage(@Payload ChatMessage message) {
        // 为消息添加时间戳,便于前端展示
        message.setTimestamp(LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
        return message; // 自动序列化为 JSON 并广播到 /topic/public
    }

    /**
     * 处理用户登录事件(可选)
     * 
     * 当客户端首次连接并订阅 /user/{username}/queue/messages 时触发
     * 可用于发送欢迎消息、在线状态更新等
     */
    @MessageMapping("/user.login")
    @SendToUser("/queue/notifications")
    public ChatMessage handleUserLogin(@Payload ChatMessage message, SimpMessageHeaderAccessor headerAccessor) {
        String username = message.getFrom(); // 从消息中获取用户名
        ChatMessage response = new ChatMessage();
        response.setFrom("系统");
        response.setContent(username + " 已上线!");
        response.setTimestamp(LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
        return response;
    }

    /**
     * 当客户端订阅 /app/hello 时,自动返回欢迎信息
     * 
     * 与 @MessageMapping 不同:
     *   - @MessageMapping:客户端主动发送消息触发
     *   - @SubscribeMapping:客户端订阅主题时自动触发(无需客户端主动发消息)
     * 
     * 适用于:首次连接时返回欢迎语、初始化数据、在线人数统计等
     */
    @SubscribeMapping("/app/hello")
    public ChatMessage handleHello() {
        ChatMessage message = new ChatMessage();
        message.setFrom("系统");
        message.setContent("欢迎使用 WebSocket 聊天室!");
        message.setTimestamp(LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
        return message;
    }
}

4. 消息实体类(ChatMessage.java

package com.example.websocketdemo.controller;

import java.time.LocalDateTime;

/**
 * 聊天消息实体类
 * 
 * 作用:定义客户端与服务端之间传输的消息结构
 * 
 * 注意:
 *   - 必须是 POJO(普通 Java 对象)
 *   - 字段名必须与前端 JSON 字段名一致(默认是 camelCase)
 *   - Spring 会自动将 JSON 反序列化为此对象
 *   - 也可以使用 @JsonProperty 注解指定字段映射
 */
public class ChatMessage {

    private String from;      // 发送者用户名
    private String content;   // 消息内容
    private String timestamp; // 发送时间(字符串格式)

    // 无参构造函数(Jackson 反序列化必需)
    public ChatMessage() {}

    // 全参构造函数(方便测试)
    public ChatMessage(String from, String content, String timestamp) {
        this.from = from;
        this.content = content;
        this.timestamp = timestamp;
    }

    // Getter 和 Setter 方法(必须提供,用于序列化/反序列化)
    public String getFrom() {
        return from;
    }

    public void setFrom(String from) {
        this.from = from;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(String timestamp) {
        this.timestamp = timestamp;
    }
}

5. 前端聊天页面(resources/static/chat.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>WebSocket 聊天室</title>
    <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1.6.1/dist/sockjs.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/stompjs@2.3.3/lib/stomp.min.js"></script>
    <style>
        body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 20px; }
        #chat-box { width: 600px; height: 400px; border: 1px solid #ccc; padding: 10px; overflow-y: auto; margin-bottom: 10px; }
        #message-input { width: 400px; padding: 8px; }
        #send-btn { padding: 8px 16px; }
        .system { color: #888; font-style: italic; }
        .me { background-color: #e3f2fd; padding: 5px 10px; border-radius: 10px; margin: 5px 0; }
        .other { background-color: #f0f0f0; padding: 5px 10px; border-radius: 10px; margin: 5px 0; }
    </style>
</head>
<body>
    <h2>🚀 WebSocket 实时聊天室</h2>
    <div id="chat-box"></div>
    <input type="text" id="message-input" placeholder="输入消息..." />
    <button id="send-btn">发送</button>
    <button id="connect-btn">连接</button>
    <button id="disconnect-btn">断开</button>

    <script>
        // ---------------------- 初始化变量 ----------------------
        let stompClient = null; // STOMP 客户端实例
        let username = null;    // 当前用户昵称

        // ---------------------- 连接 WebSocket ----------------------
        function connect() {
            // 创建 SockJS 连接(兼容性好,自动降级)
            // 服务端注册的端点是 /chat
            const socket = new SockJS('http://localhost:8080/chat');

            // 创建 STOMP 客户端
            stompClient = Stomp.over(socket);

            // 连接到服务器
            stompClient.connect({}, function (frame) {
                console.log('✅ 已连接到 WebSocket 服务器:', frame);

                // 1. 订阅公共聊天主题(接收所有人的消息)
                //   - 服务端广播到 /topic/public,客户端订阅此主题接收
                stompClient.subscribe('/topic/public', function (message) {
                    displayMessage(JSON.parse(message.body));
                });

                // 2. 订阅用户私有消息(用于接收系统通知)
                //   - 服务端使用 @SendToUser 发送,路径为 /user/{username}/queue/messages
                stompClient.subscribe('/user/queue/notifications', function (message) {
                    displayMessage(JSON.parse(message.body), 'system');
                });

                // 3. 订阅欢迎消息(测试 @SubscribeMapping)
                stompClient.subscribe('/app/hello', function (message) {
                    displayMessage(JSON.parse(message.body), 'system');
                });

                // 4. 设置当前用户名(模拟登录)
                username = prompt("请输入您的昵称:") || "游客" + Math.floor(Math.random() * 100);
                document.getElementById('message-input').placeholder = '你好,' + username + ',输入消息...';

                // 5. 向服务端发送登录消息(可选)
                stompClient.send("/app/user.login", {}, JSON.stringify({from: username}));
            }, function (error) {
                console.error('❌ 连接失败:', error);
                alert('连接失败,请检查服务端是否启动!');
            });
        }

        // ---------------------- 发送消息 ----------------------
        function sendMessage() {
            const input = document.getElementById('message-input');
            const message = input.value.trim();

            if (message && stompClient && stompClient.connected) {
                // 发送消息到服务端 /app/chat 路径
                // 服务端 @MessageMapping("/chat") 会接收此消息
                stompClient.send("/app/chat", {}, JSON.stringify({
                    from: username,     // 发送者
                    content: message, // 消息内容
                    timestamp: ""     // 时间由服务端生成
                }));

                // 清空输入框
                input.value = '';
            } else {
                alert('请先连接服务器!');
            }
        }

        // ---------------------- 显示消息到页面 ----------------------
        function displayMessage(message, type = 'other') {
            const chatBox = document.getElementById('chat-box');
            const msgDiv = document.createElement('div');

            // 根据类型设置样式
            if (type === 'system') {
                msgDiv.className = 'system';
                msgDiv.textContent = `[${message.timestamp}] ${message.from}: ${message.content}`;
            } else if (message.from === username) {
                msgDiv.className = 'me';
                msgDiv.textContent = `${message.from} (${message.timestamp}): ${message.content}`;
            } else {
                msgDiv.className = 'other';
                msgDiv.textContent = `${message.from} (${message.timestamp}): ${message.content}`;
            }

            chatBox.appendChild(msgDiv);
            chatBox.scrollTop = chatBox.scrollHeight; // 自动滚动到底部
        }

        // ---------------------- 绑定事件 ----------------------
        document.getElementById('send-btn').addEventListener('click', sendMessage);
        document.getElementById('message-input').addEventListener('keypress', function (e) {
            if (e.key === 'Enter') {
                sendMessage();
            }
        });

        document.getElementById('connect-btn').addEventListener('click', connect);
        document.getElementById('disconnect-btn').addEventListener('click', function () {
            if (stompClient && stompClient.connected) {
                stompClient.disconnect();
                console.log('👋 已断开连接');
                alert('已断开连接');
            }
        });

        // 页面加载完成后自动尝试连接
        window.onload = function () {
            connect();
        };
    </script>
</body>
</html>

6. 启动类(WebSocketDemoApplication.java

package com.example.websocketdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * Spring Boot 启动类
 * 
 * 作用:启动整个 Spring Boot 应用
 * 
 * @SpringBootApplication:组合注解,包含:
 *   - @Configuration:标记为配置类
 *   - @EnableAutoConfiguration:启用自动配置
 *   - @ComponentScan:扫描包下所有组件(Controller、Service、Config 等)
 */
@SpringBootApplication
public class WebSocketDemoApplication {
    public static void main(String[] args) {
        // 启动 Spring Boot 应用
        SpringApplication.run(WebSocketDemoApplication.class, args);
        System.out.println("🎉 WebSocket 聊天室服务已启动,请访问:http://localhost:8080/chat.html");
    }
}

五、运行与测试

✅ 启动步骤:

  1. 启动服务端

    mvn spring-boot:run
    

    控制台输出:

    🎉 WebSocket 聊天室服务已启动,请访问:http://localhost:8080/chat.html
    
  2. 打开浏览器

    • 访问 http://localhost:8080/chat.html
    • 输入昵称(如:张三)
    • 打开另一个浏览器标签页(或使用隐身模式),输入另一个昵称(如:李四)
  3. 测试效果

    • 张三发送消息 → 李四立即收到
    • 李四回复 → 张三立即收到
    • 系统自动发送欢迎语
    • 断开重连 → 重新连接后仍可接收消息

六、进阶优化建议

优化方向说明
用户认证结合 Spring Security,在连接时验证 Token,绑定用户身份
集群部署使用 Redis 作为 STOMP 消息代理(替换 enableSimpleBrokerenableStompBrokerRelay
心跳检测配置 heartbeat 避免连接被防火墙断开
消息持久化将聊天记录存入数据库,支持历史消息查询
消息限流防止刷屏,使用 @RateLimitSimpMessagingTemplate 限流
前端框架集成可与 Vue/React/Angular 集成,使用 vue-stompngx-stomp 等库

七、常见问题与解决方案

问题原因解决方案
Connection refused服务未启动或端口被占用检查控制台日志,确认端口 8080 是否被占用
Cannot connect to WebSocket endpoint前端地址错误确保 new SockJS('http://localhost:8080/chat') 与服务端 addEndpoint("/chat") 一致
404 Not Found静态资源未加载确保 chat.html 放在 src/main/resources/static/ 目录下
消息收不到订阅路径错误检查 subscribe('/topic/public') 是否与 @SendTo("/topic/public") 完全一致
跨域问题浏览器报错registerStompEndpoints 中添加 .setAllowedOriginPatterns("*")

八、总结

维度说明
核心价值实现低延迟、双向、实时通信,替代轮询和长轮询
技术优势基于 Spring 生态,开发效率高,兼容性好,文档丰富
适用场景聊天、通知、监控、协同编辑、实时数据推送
推荐架构Spring Boot + STOMP + SockJS + HTML5
生产建议集群部署时使用 Redis 作为消息代理,配合 Spring Security 做权限控制

最佳实践:优先使用 @MessageMapping + @SendTo + SockJS 模式,避免直接使用原始 WebSocket,降低开发复杂度。


附录:完整项目 GitHub 模板(可直接克隆)

git clone https://github.com/example/spring-boot-websocket-chat-demo.git
cd spring-boot-websocket-chat-demo
mvn spring-boot:run

📌 本示例已通过 Spring Boot 3.2 + Java 17 测试,可直接用于生产项目参考。


💡 结语spring-boot-starter-websocket 是实现现代 Web 实时交互的“瑞士军刀”。掌握它,你就能轻松构建微信、钉钉、飞书、股票行情、在线游戏等主流应用的实时通信功能!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙茶清欢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值