在项目中要实现一个功能点,后端解析InputStream中的数据时同步发送到前端,因此需要使用WebSocket,记录一下开发过程中遇到的问题。
一 WebSocket常用方法类
package com.ai.server.util;
import com.ai.server.service.impl.DataService;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
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.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
@Component // 交给IOC容器
@ServerEndpoint("/webSocket")
public class WebSocketService {
// 定义属性
private Session session;
//创建一个set用来存储用户
private static CopyOnWriteArraySet<WebSocketService> websockets = new CopyOnWriteArraySet<>();
/**
* 当有用户创建连接时候调用该方法
*/
@OnOpen
public void onOpen(Session session) {
// 给当前的Session赋值
this.session = session;
// 将当前对象添加到CopyOnWriteArraySet 中
websockets.add(this);
// 可以获取该session,但是其实也是一个内存地址
System.err.println("【建立连接】 用户为:" + this.session);
// 获取总数,这个不难理解,实际上这个集合的总数,就是WebSocket连接的总数
System.err.println("【建立连接】 总数为:" + websockets.size());
}
/**
* 有用户连接断开时候触发该方法
*/
@OnClose
public void onClose() {
websockets.remove(this); // 将当前的对象从集合中删除
System.err.println("【连接断开】 用户为:" + this.session);
System.err.println("【连接断开】 总数为:" + websockets.size());
}
/**
* 这个方法是客户端给服务端发送消息触发该方法
* @param message : 消息内容
*/
@OnMessage
public void onMessage(String message) throws JSONException {
System.err.println("【收到客户端发的消息】:" + message);
}
/**
* 发送消息的方法,方便后期别的service调用
*
* @param message 消息内容
*/
public void sendMessage(String message) {
for (WebSocketService websocket : websockets) { // 遍历该Set集合
System.err.println("广播消息 【给用户】 :" + websocket + "发送消息" + "【" + message + "】"); // 获取一个,在控制台打印一句话
try {
websocket.session.getBasicRemote().sendText(message); // 发送消息的方法
} catch (IOException e) {
e.getMessage();
}
}
}
}
二 WebSocket配置类
只有方法类的时候是无法成功调用的,需要有配置类
package com.ai.server.util;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
/**
* 注入ServerEndpointExporter,
* 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
三 WebSocket类中调用其他应用层方法
在WebSocket类中直接自动注入是无效的,@Resource和@Autowire都不行,在实例化之后以及调用的时候该应用层类对象为null。
查阅资料了解到由于WebSocket容器是多对象,springboot容器是单例,因此WebSocket容器相对于springboot容器是独立的,不在springboot容器管理下,所以用普通的自动注入会失效。
private static DataService dataService;
@Autowired
public void setChatService(DataService dataService) {
WebSocketService.dataService = dataService;
}
需要先实例化成静态类再注入。
四 前台部分
data() {
return {
websocket: null,
messages: ''
}
},
mounted() {
this.websocket = new WebSocket('ws://localhost:8080/webSocket')
console.log(this.websocket)
// 监听WebSocket连接打开事件
this.websocket.onopen = () => {
console.log('WebSocket connected')
}
// 监听WebSocket消息
this.websocket.onmessage = (event) => {
console.log(event.data)
// 处理接收到的消息,比如更新界面等操作
this.message = event.data
}
// 监听WebSocket关闭事件
this.websocket.onclose = () => {
console.log('WebSocket disconnected')
}
},