说明:本文介绍如何在Spirng Boot中整合WebSocket,WebSocket介绍,参考下面这篇文章:
原始方式
原始方式,指的是使用Spring Boot自己整合的方式,导入的是下面这个依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
Step1:创建WebSocket类
创建一个WebSocket服务类,如下:
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* WebSocket服务
*/
@Component
@ServerEndpoint("/ws/{sid}")
public class WebSocketServer {
// 存放会话对象
private static Map<String, Session> sessionMap = new HashMap();
/**
* 客户端连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("sid") String sid) {
System.out.println("客户端:" + sid + "建立连接");
sessionMap.put(sid, session);
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, @PathParam("sid") String sid) {
System.out.println("收到来自客户端:" + sid + "的信息:" + message);
}
/**
* 连接关闭调用的方法
*
* @param sid
*/
@OnClose
public void onClose(@PathParam("sid") String sid) {
System.out.println("连接断开:" + sid);
sessionMap.remove(sid);
}
/**
* 群发
*
* @param message
*/
public void sendToAllClient(String message) {
Collection<Session> sessions = sessionMap.values();
for (Session session : sessions) {
try {
//服务器向客户端发送消息
session.getBasicRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Step2:注入IOC中
将WebSocketServer注入到IOC容器中,如下:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* WebSocket配置类,用于注册WebSocket的Bean
*/
@Configuration
public class WebSocketConfiguration {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
Step3:创建任务
如下,创建一个服务器主动推送给客户端的任务,每5秒推送消息给客户端;
在这里插入代码片import com.hezy.websocket.WebSocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Component
public class WebSocketTask {
@Autowired
private WebSocketServer webSocketServer;
/**
* 通过WebSocket每隔5秒向客户端发送消息
*/
@Scheduled(cron = "0/5 * * * * ?")
public void sendMessageToClient() {
webSocketServer.sendToAllClient("这是来自服务端的消息:" + DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now()));
}
}
Step4:启动类增加注解
启动类需要增加@EnableWebSocket
注解,@EnableScheduling
注解是开启定时任务的,配合定时器注解@Scheduled(cron = "0/5 * * * * ?")
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
/**
* @author hezhongying
* @create 2024/7/27 14:05
*/
@SpringBootApplication
@EnableScheduling
@EnableWebSocket
public class Start {
public static void main(String[] args) {
SpringApplication.run(Start.class, args);
}
}
Step5:启动测试
启动,使用Apifox测试,选择新建WebSocket接口
填上地址,端口号是8080,这一点和后面使用第三方框架不同;
连接成功,可以看到代码中的消息定时推送到客户端;
以上就是使用Spring Boot原始的方式整合WebSocket;
使用第三方框架
使用第三方框架(GitHub地址:netty-websocket-spring-boot-starter)的方式整合WebSocket非常容易,如下:
Step1:引入依赖
在pom.xml文件中,引入下面依赖
<dependency>
<groupId>org.yeauty</groupId>
<artifactId>netty-websocket-spring-boot-starter</artifactId>
<version>0.12.0</version>
</dependency>
Step2:创建WebSocket类
在GitHub上,把类拷贝过来,如下:
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.timeout.IdleStateEvent;
import org.springframework.util.MultiValueMap;
import org.yeauty.annotation.*;
import org.yeauty.pojo.Session;
import java.io.IOException;
import java.util.Map;
@ServerEndpoint(host = "${ws.host}", port = "${ws.port}", path = "/ws")
public class MyWebSocket {
@BeforeHandshake
public void handshake(Session session, HttpHeaders headers, @RequestParam String req, @RequestParam MultiValueMap reqMap, @PathVariable String arg, @PathVariable Map pathMap){
session.setSubprotocols("stomp");
if (!"ok".equals(req)){
System.out.println("Authentication failed!");
session.close();
}
}
@OnOpen
public void onOpen(Session session, HttpHeaders headers, @RequestParam String req, @RequestParam MultiValueMap reqMap, @PathVariable String arg, @PathVariable Map pathMap){
System.out.println("new connection");
System.out.println(req);
}
@OnClose
public void onClose(Session session) throws IOException {
System.out.println("one connection closed");
}
@OnError
public void onError(Session session, Throwable throwable) {
throwable.printStackTrace();
}
@OnMessage
public void onMessage(Session session, String message) {
System.out.println(message);
session.sendText("Hello Netty!");
}
@OnBinary
public void onBinary(Session session, byte[] bytes) {
for (byte b : bytes) {
System.out.println(b);
}
session.sendBinary(bytes);
}
@OnEvent
public void onEvent(Session session, Object evt) {
if (evt instanceof IdleStateEvent) {
IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
switch (idleStateEvent.state()) {
case READER_IDLE:
System.out.println("read idle");
break;
case WRITER_IDLE:
System.out.println("write idle");
break;
case ALL_IDLE:
System.out.println("all idle");
break;
default:
break;
}
}
}
}
Step3:增加配置
在配置文件中,新增以下配置,表示websocket接收的主机和端口,如下:
# websocket配置
ws:
host: 0.0.0.0
port: 90
Step4:启动测试
启动项目,使用apifox测试
填上websocket服务地址和端口,记得加上一个参数req=ok
,因为我们上面代码里有个判断
连接成功
Message里面写需要发送的内容,发送
可以看到,发送到消息成功,同时也接收到了服务端传递过来的消息,就是说此时如果代码里需要发送消息给客户端,就可以主动发送消息过来,可以基于上面的代码写自己的业务逻辑。
另外
其中相关注解,可以查看GitHub中的文档,我也在摸索中;
总结
本文介绍了Spring Boot使用WebSocket的两种方式