星中有光 發表於 2026-1-8 09:34:06

Spring Boot 整合原生 WebSocket的实际操作

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>1、简述</li><ul class="second_class_ul"><li>1.1 什么是 WebSocket ?</li><li>1.2 WebSocket特点</li></ul><li>2、Spring Boot 整合 WebSocket</li><ul class="second_class_ul"><li>2.1 引入依赖</li><li>2.2 WebSocket 配置类</li><li>2.3 WebSocket 处理器</li><li>2.4 WebSocket 握手拦截器</li><li>2.5 WebSocketMessage 会话管理实体类</li><li>2.6 WebSocket 连接会话管理</li><li>2.7 WebSocket 服务类</li><li>2.7 WebSocket 服务实现类</li><li>2.8 Controller 对外接口</li></ul><li>3、测试</li><ul class="second_class_ul"></ul></ul></div><p class="maodian"></p><h2>1、简述</h2>
<p class="maodian"></p><h3>1.1 什么是 WebSocket ?</h3>
<p>WebSocket是一种在单个TCP连接上进行全双工、双向通信的网络协议,它实现了浏览器与服务器之间的持久性实时对话。与传统的HTTP请求-响应模式不同,WebSocket在初次握手后,双方可以随时主动发送数据。</p>
<p class="maodian"></p><h3>1.2 WebSocket特点</h3>
<table><thead><tr><th>特点维度</th><th>具体说明</th><th>带来的优势与考量</th></tr></thead><tbody><tr><td>全双工通信</td><td>在单个TCP连接上,客户端与服务器可以同时、独立地发送和接收数据。</td><td>实现了真正的实时双向对话,是构建聊天室、在线游戏、协同编辑等强交互应用的理想协议。</td></tr><tr><td>独立协议</td><td>通过一次HTTP Upgrade握手,将连接升级为独立的 ws或wss(加密) 协议进行通信。</td><td>握手后切换至专为实时交互设计的轻量级帧协议,摆脱了HTTP的无状态和头部冗余,通信效率更高。</td></tr><tr><td>持久化连接</td><td>连接一旦建立便会一直保持,直到显式关闭,期间可进行无数次数据交换。</td><td>彻底消除了HTTP的重复连接建立开销(如TCP握手、SSL协商),极大降低了延迟,并减少了服务器资源消耗。</td></tr><tr><td>低开销数据帧</td><td>数据传输采用自定义的帧格式,每个消息的协议头部额外开销极小。</td><td>特别适合高频、小数据量的通信场景(如心跳、实时坐标更新),网络利用率极高,延迟极低。</td></tr><tr><td>支持二进制与文本</td><td>可在同一连接中无缝传输文本数据和二进制数据(如ArrayBuffer, Blob)</td><td>功能全面强大,既能高效传输JSON等文本,也能直接处理文件、图片、音视频流,无需像SSE那样进行Base64编解码。</td></tr><tr><td>灵活但需自建的API</td><td>浏览器提供原生的 WebSocket API,包含 onopen, onmessage, send, close 等核心事件和方法。</td><td>为双向实时通信提供了标准化的底层构建块,控制力强。</td></tr></tbody></table>
<p class="maodian"></p><h2>2、Spring Boot 整合 WebSocket</h2>
<p class="maodian"></p><h3>2.1 引入依赖</h3>
<div class="jb51code"><pre class="brush:plain;">&lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-websocket&lt;/artifactId&gt;
&lt;/dependency&gt;</pre></div>
<p class="maodian"></p><h3>2.2 WebSocket 配置类</h3>
<div class="jb51code"><pre class="brush:java;">@Configuration
@EnableWebSocket
@Slf4j
public class WebSocketConfig implements WebSocketConfigurer {
    @Resource
    private MyWebSocketHandler myWebSocketHandler;
    @Resource
    private WebSocketAuthInterceptor authInterceptor;
    @Value("${server.port}")
    private String port;
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
      registry
                // 注册 WebSocket 处理器并设置连接路劲
                .addHandler(myWebSocketHandler, "/ceshi")
                // 添加鉴权拦截器
                .addInterceptors(authInterceptor)
                // 允许跨域(生产需限制)
                .setAllowedOrigins("*");
      log.info("======================================");
      log.info("🚀 WebSocket 模块启动完成");
      log.info("📡 WebSocket 地址:ws://localhost:{}/ceshi", port);
      log.info("🔐 鉴权方式:HandshakeInterceptor (userId)");
      log.info("🌐 跨域策略:AllowedOrigins = *");
      log.info("======================================");
    }
}</pre></div>
<p class="maodian"></p><h3>2.3 WebSocket 处理器</h3>
<div class="jb51code"><pre class="brush:java;">@Component
@Slf4j
public class MyWebSocketHandler extends TextWebSocketHandler {
    @Resource
    private WebSocketSessionManager sessionManager;
    /**
   * 建立连接成功后回调
   */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
      // 从拦截器中获取 userId
      String userId = (String) session.getAttributes().get("userId");
      // 保存连接
      sessionManager.add(userId, session);
      log.info("用户上线:{}", userId);
    }
    /**
   * 收到客户端消息时回调
   */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
      String payload = message.getPayload();
      // 心跳消息
      if ("ping".equals(payload)) {
            session.sendMessage(new TextMessage("pong"));
            return;
      }
      log.info("收到客户端消息:{}", payload);
    }
    /**
   * 连接关闭时回调
   */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
      String userId = (String) session.getAttributes().get("userId");
      // 移除连接
      sessionManager.remove(userId);
      log.info("用户离线:{}", userId);
    }
}</pre></div>
<p class="maodian"></p><h3>2.4 WebSocket 握手拦截器</h3>
<div class="jb51code"><pre class="brush:java;">@Component
public class WebSocketAuthInterceptor implements HandshakeInterceptor {
    @Override
    public boolean beforeHandshake(@NonNull ServerHttpRequest request,
                                 @NonNull ServerHttpResponse response,
                                 @NonNull WebSocketHandler handler,
                                 @NonNull Map&lt;String, Object&gt; attributes) {
      // 获取 ws://xxx/ws?userId=1001 中的参数
      String query = request.getURI().getQuery();
      if (query == null) {
            return false;
      }
      // 简单解析 userId(生产建议用 token / JWT)
      String userId = Arrays.stream(query.split("&amp;"))
                .filter(s -&gt; s.startsWith("userId="))
                .map(s -&gt; s.replace("userId=", ""))
                .findFirst()
                .orElse(null);
      // 校验失败,拒绝建立连接
      if (userId == null) {
            return false;
      }
      // 将 userId 保存到 session 属性中
      attributes.put("userId", userId);
      return true;
    }
    @Override
    public void afterHandshake(@NonNull ServerHttpRequest request,
                               @NonNull ServerHttpResponse response,
                               @NonNull WebSocketHandler handler,
                               Exception ex) {
      // 握手完成后无需处理
    }
}</pre></div>
<p class="maodian"></p><h3>2.5 WebSocketMessage 会话管理实体类</h3>
<div class="jb51code"><pre class="brush:java;">@Data
@AllArgsConstructor
@NoArgsConstructor
public class WebSocketMessage {
    /**
   * 消息类型
   * HEARTBEAT:心跳
   * NOTICE:通知类消息
   * DATA:业务数据
   * ERROR:错误提示
   */
    private String type;
    /**
   * 业务标识
   * 如:workOrder、device、alarm
   * 前端可根据 biz 分模块处理
   */
    private String biz;
    /**
   * 实际业务数据
   * 可放 Map / DTO / VO
   */
    private Object data;
}</pre></div>
<p class="maodian"></p><h3>2.6 WebSocket 连接会话管理</h3>
<div class="jb51code"><pre class="brush:java;">@Component
public class WebSocketSessionManager {
    /**
   * 保存在线 WebSocket 连接
   * key:userId
   * value:WebSocketSession
   */
    public static final Map&lt;String, WebSocketSession&gt; SESSION_MAP = new ConcurrentHashMap&lt;&gt;();
    /**
   * 新用户上线时调用
   */
    public void add(String userId, WebSocketSession session) {
      SESSION_MAP.put(userId, session);
    }
    /**
   * 用户下线 / 断开连接时调用
   */
    public void remove(String userId) {
      SESSION_MAP.remove(userId);
    }
    /**
   * 获取指定用户的连接
   */
    public WebSocketSession get(String userId) {
      return SESSION_MAP.get(userId);
    }
    /**
   * 获取所有在线连接
   */
    public Collection&lt;WebSocketSession&gt; all() {
      return SESSION_MAP.values();
    }
    /**
   * 给指定用户推送消息
   */
    public void sendToUser(String userId, String msg) throws IOException {
      WebSocketSession session = SESSION_MAP.get(userId);
      if (session != null &amp;&amp; session.isOpen()) {
            session.sendMessage(new TextMessage(msg));
      }
    }
    /**
   * 广播消息(慎用)
   */
    public void sendToAll(String msg) throws IOException {
      for (WebSocketSession session : SESSION_MAP.values()) {
            if (session.isOpen()) {
                session.sendMessage(new TextMessage(msg));
            }
      }
    }
}</pre></div>
<p class="maodian"></p><h3>2.7 WebSocket 服务类</h3>
<div class="jb51code"><pre class="brush:java;">public interface WebSocketService {
    /**
   * 获取指定的连接信息
   */
    WebSocketSession get(String userId);
    /**
   * 获取所有在线连接
   */
    Collection&lt;WebSocketSession&gt; all();
    /**
   * 给指定用户推送消息
   */
    Boolean sendToUser(String userId, String msg);
    /**
   * 广播消息(慎用)
   */
    Integer sendToAll(String msg);
}</pre></div>
<p class="maodian"></p><h3>2.7 WebSocket 服务实现类</h3>
<div class="jb51code"><pre class="brush:java;">@Service
@Slf4j
public class WebSocketServiceImpl implements WebSocketService {
    @Resource
    WebSocketSessionManager webSocketSessionManager;
    @Override
    public WebSocketSession get(String userId) {
      return webSocketSessionManager.get(userId);
    }
    @Override
    public Collection&lt;WebSocketSession&gt; all() {
      return webSocketSessionManager.all();
    }
    @Override
    public Boolean sendToUser(String userId, String msg) {
      WebSocketSession session = SESSION_MAP.get(userId);
      if (session == null || !session.isOpen()) {
            return false;
      }
      try {
            webSocketSessionManager.sendToUser(userId, msg);
            return true;
      } catch (IOException e) {
            log.error("WebSocket 推送失败 userId={}", userId, e);
            return false;
      }
    }
    @Override
    public Integer sendToAll(String msg) {
      int count = 0;
      for (WebSocketSession session : SESSION_MAP.values()) {
            if (session.isOpen()) {
                try {
                  webSocketSessionManager.sendToAll(msg);
                  count++;
                } catch (IOException e) {
                  log.warn("广播发送失败", e);
                }
            }
      }
      return count;
    }
}</pre></div>
<p class="maodian"></p><h3>2.8 Controller 对外接口</h3>
<div class="jb51code"><pre class="brush:java;">@Api(tags = "WebSocket API")
@RestController
@RequestMapping("/websocket")
public class WebSocketController {
    @Resource
    private WebSocketService webSocketService;
    @ApiOperation("获取指定用户的 WebSocket 连接信息")
    @GetMapping("/connection")
    public AjaxResult getConnection(@Parameter(name = "userId", description = "用户ID", required = true, example = "1001") String userId) {
      return AjaxResult.success(webSocketService.get(userId));
    }
    @ApiOperation("获取所有在线 WebSocket 连接")
    @GetMapping("/connections")
    public AjaxResult getAllConnections() {
      return AjaxResult.success(webSocketService.all());
    }
    @ApiOperation("给指定用户推送 WebSocket 消息")
    @PostMapping("/send/user")
    public AjaxResult sendToUser(
            @Parameter(name = "userId", description = "用户ID", required = true, example = "1001") String userId,
            @Parameter(name = "msg", description = "消息内容", required = true, example = "这是一条通知") String msg) {
      return webSocketService.sendToUser(userId, msg) ? AjaxResult.success("发送成功") : AjaxResult.error("用户不在线或发送失败");
    }
    @ApiOperation("广播 WebSocket 消息(慎用)")
    @PostMapping("/send/all")
    public AjaxResult sendToAll(
            @Parameter(name = "msg", description = "消息内容", required = true, example = "系统广播消息") String msg) {
      int count = webSocketService.sendToAll(msg);
      return AjaxResult.success("广播完成,发送给 " + count + " 个在线用户");
    }
}</pre></div>
<p class="maodian"></p><h2>3、测试</h2>
<p>我这里使用的是 ApiPost 软件进行测试,记住新建 WebSocket 进行连接。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202601/2026010809333462.png" /></p>
<p>连接成功如图:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202601/2026010809333482.png" /></p>
<p>进行心跳监测,发送消息:ping,会收到回复信息:pong 。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202601/2026010809333443.png" /></p>
<p>新建普通接口进行消息发送:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202601/2026010809333464.png" /></p>
<p>此时连接会显示收到的消息:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202601/2026010809333459.png" /></p>
<p>到此这篇关于Spring Boot 整合原生 WebSocket的文章就介绍到这了,更多相关Spring Boot 整合WebSocket内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>SpringBoot3整合WebSocket详细指南</li><li>SpringBoot整合Netty+Websocket实现消息推送的示例代码</li><li>SpringBoot&nbsp;整合WebSocket&nbsp;前端&nbsp;uniapp&nbsp;访问的详细方法</li><li>Springboot整合WebSocket实战教程</li><li>SpringBoot整合WebSocket实现后端向前端发送消息的实例代码</li><li>SpringBoot整合WebSocket的客户端和服务端的实现代码</li><li>SpringBoot整合websocket实现即时通信聊天</li><li>使用springboot整合websocket实现群聊教程</li><li>springboot整合websocket最基础入门使用教程详解</li><li>SpringBoot2.0整合WebSocket代码实例</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: Spring Boot 整合原生 WebSocket的实际操作