springboot 与 wesocket中,使用@Autowired无法自动注入service对象?
原因:Spring默认对bean的管理都是单例(singleton),和 websocket (多对象)相冲突。
项目启动时初始化,会初始化 websocket (非用户连接的),spring 同时会为其注入 service,该对象的 service 不是 null,被成功注入。但是,由于 spring 默认管理的是单例,所以只会注入一次 service。当用户建立新的连接时,系统又会创建一个新的 websocket 对象,这时不会再次注入了,所以导致只要是用户连接创建的 websocket 对象,都不能再注入了,后面的注入均为null。
解决方法
方法1:使用静态变量 加 @PostConstruct 解决
@ServerEndpoint("/websocket/{userId}")
@Component //关键点1
public class WebSocket {
@Autowired
private UserService userService;
private static WebSocket webSocket; //关键点2
@PostConstruct //关键点3
public void init(){
webSocket = this;
}
//略去其他代码...
@OnMessage
public void onMessage(String message, Session session) {
if(!StringUtils.isEmpty(message)){
try {
log.info("收到用户【{}】的消息: {}",this.userId,message);
webSocket.userService.helloService(); //关键点4
session.getBasicRemote().sendText("收到 "+this.userId+" 的消息 ");
}catch (Exception e){
e.printStackTrace();
}
}
}
}
配置完上述四个关键点,当新建立websocket连接时,@Autowired可成功注入service
方法2:使用静态变量,加set注入
@ServerEndpoint("/websocket/{userId}")
@Component //关键点1
public class WebSocket {
private static UserService userService; //关键点2
@Autowired //关键点3
public void setUserService (UserService userService){
WebSocket.userService = userService;
}
//略去其他代码...
@OnMessage
public void onMessage(String message, Session session) {
if(!StringUtils.isEmpty(message)){
try {
log.info("收到用户【{}】的消息: {}",this.userId,message);
WebSocket.userService.helloService(); //关键点4
session.getBasicRemote().sendText("收到 "+this.userId+" 的消息 ");
}catch (Exception e){
e.printStackTrace();
}
}
}
}
配置完上述四个关键点,当新建立websocket连接时,@Autowired也可成功注入service
方法3:使用工具类用于从Spring的上下文中去获取到类的实例
@ServerEndpoint("/websocket/{userId}")
@Component //关键点1
public class WebSocket {
private static UserService userService = SpringContextHolder.getBean(UserService.class); //关键点2
//略去其他代码...
@OnMessage
public void onMessage(String message, Session session) {
if(!StringUtils.isEmpty(message)){
try {
log.info("收到用户【{}】的消息: {}",this.userId,message);
userService.helloService(); //关键点3
session.getBasicRemote().sendText("收到 "+this.userId+" 的消息 ");
}catch (Exception e){
e.printStackTrace();
}
}
}
}
工具类SpringContextHolder
此工具类用于从Spring的上下文中去获取到类的实例
@Component
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
private static ApplicationContext applicationContext = null;
/**
* 取得存储在静态变量中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
assertContextInjected();
return applicationContext;
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
assertContextInjected();
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType) {
assertContextInjected();
return applicationContext.getBean(requiredType);
}
/**
* 清除SpringContextHolder中的ApplicationContext为Null.
*/
public static void clearHolder() {
applicationContext = null;
}
/**
* 实现ApplicationContextAware接口, 注入Context到静态变量中.
*/
@Override
public void setApplicationContext(ApplicationContext appContext) {
applicationContext = appContext;
}
/**
* 实现DisposableBean接口, 在Context关闭时清理静态变量.
*/
@Override
public void destroy() throws Exception {
SpringContextHolder.clearHolder();
}
/**
* 检查ApplicationContext不为空.
*/
private static void assertContextInjected() {
Validate.validState(applicationContext != null, "applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");
}
}
配置完上述三个关键点,当新建立websocket连接时,@Autowired也可成功注入service