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 客户端发送给服务端路径

相关推荐
你不是我我1 分钟前
【Java 开发日记】我们来说一说 Redis IO 多路复用模型
java·开发语言·redis
SadSunset4 分钟前
(13)复杂查询
java·笔记·架构·mybatis
浩瀚地学4 分钟前
【Java】ArrayList
java·开发语言·经验分享·笔记
阿杰同学12 分钟前
Java 设计模式 面试题及答案整理,最新面试题
java·开发语言·设计模式
这样の我13 分钟前
java 模拟chrome指纹 处理tls extension顺序
java·开发语言·chrome
vx_bisheyuange14 分钟前
基于SpringBoot的游戏交易系统
spring boot·后端·游戏·毕业设计
Genevieve_xiao17 分钟前
【数据结构与算法】【xjtuse】面向考纲学习(下)
java·数据结构·学习·算法
curd_boy19 分钟前
IM 顶层设计
websocket·架构·信息与通信
4311媒体网19 分钟前
php和c++哪个更好学?C++难学吗?
java·c++·php
毕设源码-朱学姐25 分钟前
【开题答辩全过程】以 基于SpringBoot的流行音乐网站的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端