Skip to main content

SpringBoot 使用@ServerEndpoint 无法依赖注入问题解决 SpringBoot webSocket 配置 不能注入(@Autowired)解决方法

问题:

spring 或 springboot 的 websocket 里面使用 @Autowired 注入 service 或 bean 时,报 java.lang.NullPointerException 异常,service 为 null(实际上并不是不能被注入)

解决:

将要注入的 service 改成 static,然后添加一个 set 方法(重要:这个 set 方法一定不能是 static 的)就不会为 null 了。

详细:

spring 管理的都是单例(singleton),和 websocket (多对象)相冲突。

项目启动时初始化,会初始化 websocket (非用户连接的),spring 同时会为其注入 service,该对象的 service 不是 null,被成功注入。但是,由于 spring 默认管理的是单例,所以只会注入一次 service。当新用户进入聊天时,系统又会创建一个新的 websocket 对象,这时矛盾出现了:spring 管理的都是单例,不会给第二个 websocket 对象注入 service,所以导致只要是用户连接创建的 websocket 对象,都不能再注入了。

import cn.hutool.core.util.StrUtil;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
* @author : qingbomy
* @email :
* @date : 2023/11/22:12:53 周三
*/
@ServerEndpoint("/xxx/log")
@Slf4j
@Component
@Data
public class LogServer {
/**
* 日志文件位置
*/
@Value("${bxt.config.websocket.log-file-path : null}")
private String logFilePath;
/**
* 如果配置文件配置了命令,直接执行命令,不关心logFilePath
*/
@Value("${bxt.config.websocket.cmd : null}")
private String cmd;
private Process process;
private InputStream inputStream;
@SuppressWarnings("all")
public static final ExecutorService EXECUTOR_SERVICE = Executors.newCachedThreadPool();
private static Test test;
@Autowired
public void setTest(Test test){
//不能注入的问题
LogServer.test=test;
}
@OnOpen
public void onOpen(Session session) {
test.getLogFilePath();
test.getCmd();
log.info("准备连接...");
if (StrUtil.isEmpty(this.logFilePath) && StrUtil.isEmpty(this.cmd)) {
throw new RuntimeException("请在配置文件配置日志路径 bxt.webSocket.log.logFilePath或bxt.webSocket.log.cmd");
}
Map<String, List<String>> params = session.getRequestParameterMap();
if (params.containsKey("logFilePath")) {
setLogFilePath(params.get("logFilePath").get(0));
}
//window系统 tail命令需要添加tail.exe小工具到system32
if (StrUtil.isEmpty(getCmd())) {
setCmd("tail -f " + getLogFilePath());
}
log.debug(String.format("show log cmd >> %s", getCmd()));
try {
this.process = Runtime.getRuntime().exec(getCmd());
this.inputStream = this.process.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
EXECUTOR_SERVICE.execute(() -> {
String line;
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(inputStream));
while ((line = reader.readLine()) != null) {
session.getBasicRemote().sendText(line + "<br>");
}
} catch (IOException e) {
e.printStackTrace();
}
});

}

@OnMessage
public void onMessage(String message, Session session) throws IOException {
log.debug(String.format("socket onmessage ==> 接收到信息:%s", message));
session.getBasicRemote().sendText("接收到信息"+message );
}

@OnClose
public void onClose(Session session) {
this.close();
log.debug("socket已关闭");
}

@OnError
public void onError(Throwable thr) {
this.close();
log.debug(String.format("socket异常,errorMessage:%s", thr.getMessage()));
}

private void close() {
//这里应该先停止命令, 然后再关闭流
if (process != null) {
process.destroy();
}

try {
if (Objects.nonNull(inputStream)) {
inputStream.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}

}