SpringBoot使用@ServerEndpoint无法@Autowired依赖注入问题解决
问题
websocket里面使用@Autowired注入service或bean时,报空指针异常。
@Component
@ServerEndpoint("/groupChat/{groupNo}/{uuid}")
public class GroupChatWebsocket {
@Autowired
private RedisTemplateUtil redisTemplateUtil;
}
原因
spring管理的都是单例(singleton)和 websocket (多对象)相冲突。
需要了解一个事实:websocket 是多对象的,每个用户的聊天客户端对应 java 后台的一个 websocket 对象,前后台一对一(多对多)实时连接,
所以 websocket 不可能像 servlet 一样做成单例的,让所有聊天用户连接到一个 websocket对象,这样无法保存所有用户的实时连接信息。
可能 spring 开发者考虑到这个问题,没有让 spring 创建管理 websocket ,而是由 java 原来的机制管理websocket ,所以用户聊天时创建的
websocket 连接对象不是 spring 创建的,spring 也不会为不是他创建的对象进行依赖注入,所以如果不用static关键字,每个 websocket 对象的 service 都是 null。
解决方案
- 方法一 使用static静态对象
private static RedisTemplateUtil redisUtil;
@Autowired
public static void setRedisTemplateUtil(RedisTemplateUtil redisUtil) {
WebSocketServer.redisUtil = redisUtil;
}
- 方法二 动态的从spring容器中取出RedisTemplateUtil 推荐方案
package cn.pconline.framework.util;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* 获取spring容器
* 当一个类实现了这个接口ApplicationContextAware之后,这个类就可以方便获得ApplicationContext中的所有bean。
* 换句话说,这个类可以直接获取spring配置文件中所有有引用到的bean对象
* 前提条件需作为一个普通的bean在spring的配置文件中进行注册
*/
public class SpringCtxUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringCtxUtils.applicationContext = applicationContext;
}
public static <T> T getBean(Class<T> type) {
try {
return applicationContext.getBean(type);
} catch (NoUniqueBeanDefinitionException e) { //出现多个,选第一个
String beanName = applicationContext.getBeanNamesForType(type)[0];
return applicationContext.getBean(beanName, type);
}
}
public static <T> T getBean(String beanName, Class<T> type) {
return applicationContext.getBean(beanName, type);
}
}