Spring Boot 3.x WebSocket 实战教程

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

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

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

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

二、环境准备与依赖

本教程基于 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-client 和 stompjs 库进行连接。

  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 服务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值