一、实现目标
1、在线聊天,客服聊天,聊天室
2、业务数据实时展示,自动更新
二、实践步骤
以下是为了实现:业务数据实时展示,自动更新
1. Maven引入
org.springframework.boot
spring-boot-starter-websocket
复制代码
2. 创建配置文件
WebSocketConfig
/**
* 开启WebSocket支持
* @author zhengkai
*/
@Configuration
public class WebSocketConfig{
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
复制代码
3. 创建服务端
@ServerEndpoint(value = "/websocket")
@Component
public class WebSocketServer{
private final Logger log = LoggerFactory.getLogger(this.getClass());
//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
private static CopyOnWriteArraySet webSocketSet = new CopyOnWriteArraySet();
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session){
this.session = session;
webSocketSet.add(this);
sendMessage("连接成功");
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose(){
webSocketSet.remove(this);
log.info("有一连接关闭!");
}
/**
* 收到客户端消息后调用的方法
*
* @param queryType 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String queryType){
log.info("收到信息:" + queryType);
//群发消息
for (WebSocketServer item : webSocketSet) {
item.sendMessage("hello");
}
}
/**
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error){
log.error("发生错误");
error.printStackTrace();
}
/**
* 实现服务器主动推送
*/
public void sendMessage(Object message){
try {
this.session.getBasicRemote().sendText(JSON.toJSONString(message));
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 实现服务器主动群发消息
*/
public static void sendInfo(Object message){
for (WebSocketServer item : webSocketSet) {
item.sendMessage(message);
}
}
}
复制代码
4. 前端发起websocket请求
var socket;
if(typeof(WebSocket) == "undefined") {
console.log("您的浏览器不支持WebSocket");
}else{
console.log("您的浏览器支持WebSocket");
//实现化WebSocket对象,指定要连接的服务器地址与端口 建立连接
socket = new WebSocket("ws://localhost:8080/webscoket");
//打开事件
socket.onopen = function() {
console.log("Socket 已打开");
//socket.send("这是来自客户端的消息" + location.href + new Date());
};
//获得消息事件
socket.onmessage = function(msg) {
console.log(msg.data);
//发现消息进入 开始处理前端触发逻辑
};
//关闭事件
socket.onclose = function() {
console.log("Socket已关闭");
};
//发生了错误事件
socket.onerror = function() {
alert("Socket发生了错误");
//此时可以尝试刷新页面
}
}
复制代码
三、实践中会遇到的问题
1. 前端发起websocket请求, 却无法连接websocket
错误一:被权限拦截,例如spring security
解决方式:放开权限或允许访问
// spring security
.antMatchers(
"/websocket"
)
.permitAll()
复制代码错误二:请求的websocket地址错误
@ServerEndpoint(value = "/websocket")
对应的地址是:ws://localhost:8080/webscoket
复制代码错误三:被AOP拦截
@Pointcut("execution(public * com.xxx.xxx.controller..*(..))")
public void webLog() {
}
复制代码
解决办法
// 排除拦截
@Pointcut("execution(public * com.xxx.xxx.controller..*(..)) && !execution(public * com.xxx.xxx.controller.xxx*(..))")
public void webLog() {
}
复制代码
2. 在WebSocketServer中,无法注入Bean,无法使用@Autowired
即你按如下方式直接使用@Autowired,会报错
@ServerEndpoint(value = "/websocket")
@Component
public class WebSocketServer{
@Autowired
ChartDataService chartDataService;
}
复制代码在WebSocket中, 因 SpringBoot+WebSocket 对每个客户端连接都会创建一个 WebSocketServer(@ServerEndpoint 注解对应的) 对象,Bean 注入操作会被直接略过,因而手动注入一个全局变量(即你需要注入的bean)
@Configuration
public class WebSocketConfig{
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
// 在这里注入你需要的bean
@Autowired
public void setMessageService(ChartDataService chartDataService){
WebSocketServer.chartDataService = chartDataService;
// ...
}
}
复制代码
使用你注入的bean
@ServerEndpoint(value = "/websocket")
@Component
public class WebSocketServer{
public static ChartDataService chartDataService;
复制代码
此方式经实践,有效
1.使用@Inject
@ServerEndpoint("/ws")
public class MyWebSocket {
@Inject
private ObjectMapper objectMapper;
}
复制代码2.使用SpringConfigurator
@ServerEndpoint(value = "/ws", configurator = SpringConfigurator.class)
复制代码3.使用@PostConstruct
@PostConstruct
public void init(){
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
复制代码经过实践,上述三种方式,对于我而言,均无效