Spring集成WebSocket
Spring集成WebSocket归纳
一、集成 spring-boot-starter-websocket
场景:通常用于在Java项目中做socket服务器
1.添加Maven依赖
<dependencies><!-- 只需要这一个依赖,其他都自动包含 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
</dependencies>
2.启用 @ServerEndpoint 支持
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 {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}
3.编写 WebSocket 服务端(使用 @ServerEndpoint)
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<>();@OnOpenpublic void onOpen(Session session) {sessions.add(session);System.out.println("新客户端连接: " + session.getId());}@OnMessagepublic 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();}}}}@OnMessagepublic void onBinaryMessage(byte[] data, Session session) {// 可处理二进制消息}
}
4.Spring Boot 启动类
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.前端页面
<!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依赖
<dependency><groupId>org.java-websocket</groupId><artifactId>Java-WebSocket</artifactId><version>1.5.3</version> <!-- 请使用最新版本 -->
</dependency>
2.继承 WebSocketClient 实现客户端
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}@Overridepublic void onOpen(ServerHandshake handshake) {System.out.println("WebSocket 连接已建立");System.out.println("握手信息: " + handshake.getHttpStatusMessage());}@Overridepublic void onMessage(String message) {System.out.println("收到消息: " + message);}@Overridepublic void onClose(int code, String reason, boolean remote) {System.out.println("连接关闭,Code: " + code + ", 原因: " + reason + ", 是否远程关闭: " + remote);}@Overridepublic void onError(Exception ex) {System.err.println("发生错误: " + ex.getMessage());ex.printStackTrace();}// 可选:重写 onMessage 处理二进制消息// @Override// public void onMessage(ByteBuffer bytes) { ... }
}
3.启动客户端连接服务器
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 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)核心
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 端点*/@Overridepublic void registerStompEndpoints(StompEndpointRegistry registry) {registry.addEndpoint("/user/ws") // 客户端连接的 WebSocket 路径.withSockJS(); // 支持降级到 SockJS(兼容性更好)}/*** 配置消息代理*/@Overridepublic void configureMessageBroker(MessageBrokerRegistry registry) {registry.enableSimpleBroker("/topic"); // 订阅路径前缀,用于广播消息registry.setApplicationDestinationPrefixes("/app"); // 应用消息前缀(发给后端)}
}
3.创建消息模型(Greeting.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)
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 {@Autowiredprivate SimpMessagingTemplate messagingTemplate;/*** 接收客户端发送的消息:/app/greeting*/@MessageMapping("/greeting")public void handleGreeting(String name) {// 广播消息到所有订阅了 /topic/greeting 的客户端messagingTemplate.convertAndSend("/topic/greeting", new Greeting("Hello, " + name + " !"));}
}
5.启动类
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.前端页面
<!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'); // 连接到 /wsstompClient = 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 客户端发送给服务端路径
