Spring Boot 3.x WebSocket 实战教程

一、什么是 WebSocket?为什么需要它?

WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。与传统的 HTTP 请求-响应模式不同,WebSocket 允许服务器主动向客户端推送数据,非常适合实时性要求高的场景。

特性 HTTP WebSocket
通信模式 请求-响应(被动) 全双工(双向主动)
连接状态 无状态,短连接 有状态,持久连接
实时性 低(需轮询,延迟高) 高(毫秒级推送)
头部开销 大(每次携带完整头信息) 小(握手后仅需少量字节)

典型应用场景:即时聊天室、股票行情推送、在线协作编辑、游戏实时状态同步。

二、环境准备与依赖

本教程基于 Spring Boot 3.x (对应 Spring Framework 6.x)和 JDK 17+

1、创建项目

使用 Spring Initializr 创建项目,勾选以下依赖:

  • Spring Web
  • Spring WebSocket
  • Lombok(可选,用于简化代码)

2、Maven 依赖

确保 pom.xml 中包含以下核心依赖:

复制代码
<dependencies>
    <!-- Web基础依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- WebSocket核心依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
</dependencies>
三、核心实现:基于 STOMP 的消息代理模式

Spring 提供了基于 STOMP 协议的 WebSocket 支持,它比原生 API 更易于使用,类似于消息队列的发布/订阅模式。

1、配置 WebSocket

创建一个配置类,启用 WebSocket 消息代理并注册端点。

复制代码
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 registry) {
        // 启用简单的内存消息代理,客户端订阅以 "/topic" 开头的目的地
        registry.enableSimpleBroker("/topic");
        // 设置客户端发送消息的前缀,即 "/app"
        registry.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        // 注册端点 "/ws",客户端将通过此路径建立连接
        // withSockJS() 提供了对不支持 WebSocket 浏览器的降级支持
        registry.addEndpoint("/ws").withSockJS();
    }
}

2、编写消息控制器

创建一个控制器来处理客户端的消息发送和广播。

复制代码
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;

@Controller
public class ChatController {

    /**
     * 处理客户端发送的消息
     * @MessageMapping("/chat") 对应客户端发送目标 "/app/chat"
     * @SendTo("/topic/messages") 将返回值广播给所有订阅 "/topic/messages" 的客户端
     */
    @MessageMapping("/chat")
    @SendTo("/topic/messages")
    public ChatMessage sendChatMessage(ChatMessage message) {
        // 这里可以添加业务逻辑,如保存消息到数据库
        System.out.println("收到消息: " + message.getContent());
        return message;
    }
}

3、定义消息实体

复制代码
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ChatMessage {
    private String username;
    private String content;
    private long timestamp;
}
四、前端客户端连接(HTML + JavaScript)

Spring WebSocket 默认支持 SockJS,前端可以使用 sockjs-clientstompjs 库进行连接。

1、引入依赖

在 HTML 文件中引入以下 CDN 资源:

复制代码
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>

2、连接与通信代码

复制代码
<script type="text/javascript">
    var stompClient = null;

    // 1. 建立连接
    function connect() {
        // 创建 SockJS 对象,指向后端注册的端点 /ws
        var socket = new SockJS('/ws');
        // 使用 STOMP 协议包装
        stompClient = Stomp.over(socket);
        
        stompClient.connect({}, function (frame) {
            console.log('连接成功: ' + frame);
            // 2. 订阅消息主题
            stompClient.subscribe('/topic/messages', function (message) {
                // 接收服务器广播的消息
                showMessage(JSON.parse(message.body));
            });
        });
    }

    // 3. 发送消息
    function sendMessage() {
        var messageContent = document.getElementById('inputMessage').value;
        var chatMessage = {
            username: 'User_' + Math.floor(Math.random() * 1000),
            content: messageContent,
            timestamp: new Date().getTime()
        };
        // 发送到 /app/chat (对应后端的 @MessageMapping("/chat"))
        stompClient.send("/app/chat", {}, JSON.stringify(chatMessage));
    }

    function showMessage(message) {
        var response = document.getElementById('response');
        var p = document.createElement('p');
        p.innerText = message.username + ": " + message.content;
        response.appendChild(p);
    }
    
    // 页面加载完成后连接
    window.onload = connect;
</script>
五、进阶:使用原生 WebSocketHandler

如果你不需要 STOMP 协议,只想处理简单的文本消息,可以使用 WebSocketHandler

1、自定义 Handler

复制代码
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.util.concurrent.CopyOnWriteArrayList;

@Component
public class MyCustomHandler extends TextWebSocketHandler {

    // 线程安全的会话列表
    private static final CopyOnWriteArrayList<WebSocketSession> sessions = new CopyOnWriteArrayList<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        sessions.add(session);
        session.sendMessage(new TextMessage("欢迎连接!当前在线人数: " + sessions.size()));
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        // 广播消息给所有连接者
        for (WebSocketSession s : sessions) {
            if (s.isOpen()) {
                s.sendMessage(new TextMessage("广播: " + message.getPayload()));
            }
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        sessions.remove(session);
    }
}

2、注册 Handler

在配置类中注入并注册:

复制代码
@Configuration
@EnableWebSocket // 注意:这里启用原生 WebSocket 支持
public class NativeWebSocketConfig implements WebSocketConfigurer {

    @Autowired
    private MyCustomHandler myCustomHandler;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        // 注册路径 /ws-native,允许跨域
        registry.addHandler(myCustomHandler, "/ws-native").setAllowedOrigins("*");
    }
}
六、常见问题排查
  • 404 Not Found :检查 @EnableWebSocketMessageBroker@EnableWebSocket 是否遗漏,以及端点路径 /ws 是否正确。

  • 跨域问题 :确保在 registerWebSocketHandlers 中配置了 .setAllowedOrigins("*") 或指定具体域名。

  • 握手失败:如果是生产环境使用 Nginx 反向代理,需要配置 Upgrade 头:

    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

通过以上步骤,你可以在 Spring Boot 3.x 中快速搭建起稳定高效的 WebSocket 服务。

相关推荐
FelixBitSoul1 天前
缓存淘汰策略全解:从原理到手写实现(Java / Go / Python)
后端·面试
AI人工智能+电脑小能手1 天前
【大白话说Java面试题】【Java基础篇】第29题:静态代理和动态代理的区别是什么
java·开发语言·后端·面试·代理模式
小编码上说1 天前
LSH(局部敏感哈希)分桶,海量数据下的相似性搜索解决方案
java·spring boot·缓存·langchain4j·lsh·局部敏感哈希·ai调用优化
计算机_毕业设计1 天前
java-springboot数字藏品系统 基于 SpringBoot 的区块链数字艺术品交易平台 Java 微服务架构下的加密藏品展示与拍卖系统计算机毕业设计
java·spring boot·课程设计
时空自由民.1 天前
WebSocket 协议介绍
网络·websocket·网络协议
dovens1 天前
SpringBoot集成MQTT客户端
java·spring boot·后端
❀͜͡傀儡师1 天前
Spring Boot 集成 RocksDB 实战:打造高性能 KV 存储加速层
java·spring boot·后端·rocksdb
TeamDev1 天前
如何在 DotNetBrowser 中使用本地 AI 模型
前端·后端·.net
Rust语言中文社区1 天前
【Rust日报】2026-05-02 Temper - 用 Rust 编写的 Minecraft 服务器项目发布 0.1.0 版
运维·服务器·开发语言·后端·rust