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