Spring集成WebSocket

Spring集成WebSocket归纳

一、集成 spring-boot-starter-websocket

场景:通常用于在Java项目中做socket服务器

1.添加Maven依赖
xml 复制代码
<dependencies>
  <!-- 只需要这一个依赖,其他都自动包含 -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
</dependencies>
2.启用 @ServerEndpoint 支持
java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * 启用标准 JSR-356 WebSocket 支持(@ServerEndpoint)
 */
@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}
3.编写 WebSocket 服务端(使用 @ServerEndpoint)
java 复制代码
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * WebSocket 服务端点
 * 注意:包是 javax.websocket.*(Spring Boot 2.x)
 *       如果是 Spring Boot 3.x,则是 jakarta.websocket.*
 */
@ServerEndpoint("/ws/hello")
public class HelloWebSocket {

    private static final CopyOnWriteArrayList<Session> sessions = new CopyOnWriteArrayList<>();

    @OnOpen
    public void onOpen(Session session) {
        sessions.add(session);
        System.out.println("新客户端连接: " + session.getId());
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("收到消息: " + message);
        // 广播给所有客户端
        for (Session s : sessions) {
            if (s.isOpen()) {
                try {
                    s.getBasicRemote().sendText("服务端回复: " + message);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @OnMessage
    public void onBinaryMessage(byte[] data, Session session) {
        // 可处理二进制消息
    }
}
4.Spring Boot 启动类
java 复制代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class WebSocketApplication {
    public static void main(String[] args) {
        SpringApplication.run(WebSocketApplication.class, args);
    }
}
5.前端页面
html 复制代码
<!DOCTYPE html>
<html>
  <head>
    <title>WebSocket @ServerEndpoint 测试</title>
  </head>
  <body>
    <h3>打开浏览器控制台查看消息</h3>
    <script>
      const ws = new WebSocket("ws://localhost:8080/ws/hello");
      ws.onopen = () => {
        console.log("WebSocket 连接成功");
        ws.send("Hello from browser!");
      };
      ws.onmessage = (event) => {
        console.log("收到消息: " + event.data);
      };
      ws.onerror = (error) => {
        console.error("WebSocket 错误: ", error);
      };
    </script>
  </body>
</html>
6.说明
Spring Boot 版本 WebSocket 包名 说明
2.x javax.websocket.* Java EE 风格
3.x jakarta.websocket.* Jakarta EE 风格(Java EE 的延续)

但无论哪个版本,都不需要手动添加 javax.websocket-api 或 jakarta.websocket-api 依赖。

二、继承 WebSocketClient 实现客户端

场景:通常用于在Java程序中,使用websocket连接远程服务器,进行通信

1.添加Maven依赖
xml 复制代码
<dependency>
  <groupId>org.java-websocket</groupId>
  <artifactId>Java-WebSocket</artifactId>
  <version>1.5.3</version> <!-- 请使用最新版本 -->
</dependency>
2.继承 WebSocketClient 实现客户端
java 复制代码
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.java_websocket.drafts.Draft_6455;

import java.net.URI;
import java.net.URISyntaxException;

public class MyWebSocketClient extends WebSocketClient {

    public MyWebSocketClient(URI serverUri) {
        super(serverUri, new Draft_6455()); // 使用标准 WebSocket 协议 Draft
    }

    @Override
    public void onOpen(ServerHandshake handshake) {
        System.out.println("WebSocket 连接已建立");
        System.out.println("握手信息: " + handshake.getHttpStatusMessage());
    }

    @Override
    public void onMessage(String message) {
        System.out.println("收到消息: " + message);
    }

    @Override
    public void onClose(int code, String reason, boolean remote) {
        System.out.println("连接关闭,Code: " + code + ", 原因: " + reason + ", 是否远程关闭: " + remote);
    }

    @Override
    public void onError(Exception ex) {
        System.err.println("发生错误: " + ex.getMessage());
        ex.printStackTrace();
    }

    // 可选:重写 onMessage 处理二进制消息
    // @Override
    // public void onMessage(ByteBuffer bytes) { ... }
}
3.启动客户端连接服务器
java 复制代码
public class WebSocketClientExample {
    public static void main(String[] args) {
        try {
            // 连接到本地运行的 WebSocket 服务端(例如 ws://localhost:8080/websocket)
            URI uri = new URI("ws://localhost:8080/websocket");

            MyWebSocketClient client = new MyWebSocketClient(uri);
            client.connect(); // 异步连接

            // 等待连接建立(简单等待,生产环境建议用 latch 或事件机制)
            Thread.sleep(3000);

            if (client.isOpen()) {
                client.send("Hello from Java-WebSocket Client!");
            }

            // 保持程序运行,接收消息
            System.out.println("按回车键退出...");
            System.in.read();

            // 关闭连接
            client.close();

        } catch (URISyntaxException | InterruptedException e) {
            e.printStackTrace();
        } catch (Exception e) {
            System.err.println("启动客户端失败: " + e.getMessage());
        }
    }
}
4.说明
方法 说明
onOpen() 连接成功建立时触发
onMessage(String) 收到文本消息时触发
onMessage(ByteBuffer) 收到二进制消息时触发(可重写)
onClose() 连接关闭时触发,包括正常关闭和异常断开
onError() 发生网络错误、解析错误等时触发
send(String) 发送文本消息
send(ByteBuffer) 发送二进制消息
close() 主动关闭连接

三、实现 WebSocketMessageBrokerConfigurer 接口

场景:通常用于支持PC或H5页面连接通信的服务器

1.添加 Maven 依赖
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
  <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>2.7.12</version>
    <relativePath/>
  </parent>

  <dependencies>
    <!-- Spring Boot Web Starter -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Spring Boot WebSocket Starter -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>

    <!-- STOMP over WebSocket 支持 -->
    <dependency>
      <groupId>org.webjars</groupId>
      <artifactId>webjars-locator-core</artifactId>
    </dependency>
    <dependency>
      <groupId>org.webjars</groupId>
      <artifactId>sockjs-client</artifactId>
      <version>1.5.1</version>
    </dependency>
    <dependency>
      <artifactId>stomp-websocket</artifactId>
      <groupId>org.webjars</groupId>
      <version>2.3.4</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>
2.配置 WebSocket(WebSocketConfig.java)核心
java 复制代码
package com.example.demo.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;

@Configuration
@EnableWebSocketMessageBroker  // 启用 WebSocket 消息代理功能
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    /**
     * 注册 STOMP 端点
     */
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/user/ws")               // 客户端连接的 WebSocket 路径
        .withSockJS();                   // 支持降级到 SockJS(兼容性更好)
    }

    /**
     * 配置消息代理
     */
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic");   // 订阅路径前缀,用于广播消息
        registry.setApplicationDestinationPrefixes("/app"); // 应用消息前缀(发给后端)
    }
}
3.创建消息模型(Greeting.java)
java 复制代码
package com.example.demo;

public class Greeting {
    private String content;

    public Greeting() {}

    public Greeting(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}
4.创建控制器(GreetingController.java)
java 复制代码
package com.example.demo.controller;

import com.example.demo.Greeting;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;

@Controller
public class GreetingController {

    @Autowired
    private SimpMessagingTemplate messagingTemplate;

    /**
     * 接收客户端发送的消息:/app/greeting
     */
    @MessageMapping("/greeting")
    public void handleGreeting(String name) {
        // 广播消息到所有订阅了 /topic/greeting 的客户端
        messagingTemplate.convertAndSend("/topic/greeting", new Greeting("Hello, " + name + " !"));
    }
}
5.启动类
java 复制代码
package com.example.demo;

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

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
6.前端页面
html 复制代码
<!DOCTYPE html>
<html>
<head>
    <title>Spring Boot WebSocket Demo</title>
    <script src="/webjars/sockjs-client/sockjs.min.js"></script>
    <script src="/webjars/stomp-websocket/stomp.min.js"></script>
</head>
<body>
<h2>WebSocket 聊天测试</h2>
<button onclick="connect()">连接</button>
<button onclick="disconnect()">断开</button><br><br>

用户名: <input type="text" id="name" /><br><br>
<button onclick="sendName()">发送问候</button><br><br>

<div id="greeting"></div>

<script>
    let stompClient = null;

    function connect() {
        const socket = new SockJS('/ws');  // 连接到 /ws
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function (frame) {
            console.log('Connected: ' + frame);
            stompClient.subscribe('/topic/greeting', function (greeting) {
                const data = JSON.parse(greeting.body);
                document.getElementById('greeting').innerHTML += `<p>${data.content}</p>`;
            });
        });
    }

    function disconnect() {
        if (stompClient) {
            stompClient.disconnect();
        }
        console.log("Disconnected");
    }

    function sendName() {
        const name = document.getElementById('name').value;
        stompClient.send("/app/greeting", {}, name);
    }
</script>
</body>
</html>
7.说明

/topic 服务端广播给客户端路径

/app 客户端发送给服务端路径

相关推荐
Brookty3 小时前
【算法】前缀和(二)使用
java·学习·算法·前缀和·动态规划·1024程序员节
兜兜风d'3 小时前
基于 Spring Boot + RabbitMQ 实现应用通信
spring boot·rabbitmq·java-rabbitmq·1024程序员节
进击的圆儿3 小时前
网络编程实战02·从零搭建Epoll服务器
1024程序员节
计算衎3 小时前
Jenkins上实现CI集成软件信息Teams群通知案例实现。
python·jenkins·1024程序员节·microsoft azure·teams消息群通知·微软 graph api
爱看老照片3 小时前
计算机端口
1024程序员节
go_bai3 小时前
Linux_基础IO(2)
linux·开发语言·经验分享·笔记·学习方法·1024程序员节
西部森林牧歌3 小时前
拒绝笨重,一款轻量、极致简洁的开源CI/CD工具 - Arbess
1024程序员节·cicd·tiklab·arbess
浆果02073 小时前
【图像超分】论文复现:轻量化超分 | RLFN的Pytorch源码复现,跑通源码,整合到EDSR-PyTorch中进行训练、测试
人工智能·python·深度学习·超分辨率重建·1024程序员节
Arnold.Shen4 小时前
Vcenter7使用主机配置文件重置ESXI主机 root 密码
1024程序员节