spring 或 springboot 的 websocket 里面使用 @Autowired 注入 service 或 bean 时,报空指针异常,service 为 null(并不是不能被注入)。
解决方法:
- 将要注入的 service 改成 static,就不会为null了。
- SpringUtil,从applicationContext中获取,为每次连接创建的新websocket对象的成员变量赋值
方法一:
@Controller
@ServerEndpoint(value="/chatSocket")
public class ChatSocket {
// 这里使用静态,让 service 属于类
private static ChatService chatService;
// 注入的时候,给类的 service 注入
//方法参数chatService会自动从容器中取得并赋值
@Autowired
public void setChatService(ChatService chatService) {
ChatSocket.chatService = chatService;
}
}
方法二:
//使用
private DeviceMapper deviceMapper = (DeviceMapper) SpringUtil.getBean("deviceMapper");
@Component
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(SpringUtil.applicationContext == null){
SpringUtil.applicationContext = applicationContext;
}
}
//获取applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
//通过name获取 Bean.
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}
//通过class获取Bean.
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
//通过name,以及Clazz返回指定的Bean
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
}
}
本质原因:
spring管理的都是单例(singleton)和 websocket (多对象)相冲突。
每个连接都会创建一个websocket 对象
websocket 是多对象的,每个用户的聊天客户端对应 java 后台的一个 websocket 对象,前后台一对一(多对多)实时连接,所以 websocket 不可能像 servlet 一样做成单例的,让所有聊天用户连接到一个 websocket对象,这样无法保存所有用户的实时连接信息。可能 spring 开发者考虑到这个问题,没有让 spring 创建管理 websocket ,而是由 java 原来的机制管理websocket ,所以用户聊天时创建的 websocket 连接对象不是 spring 创建的,spring 也不会为不是他创建的对象进行依赖注入,所以如果不用static关键字,每个 websocket 对象的 service 都是 null。
详细解释(按上面我写的代码,假设属性使用了 static):
初始化:项目启动时,spring 工厂会创建 websocket 的单例对象(此时注解合法,spring 就会为 ChatSocket 类的属性 ChatService 进行注入,并创建一个单例对象,spring 并不知道 websocket 的特殊意义,只是该类的注解合法,便会进行操作,与其他 controller 进行的操作一模一样),因此 chatService 不是 null。
聊天时:当新用户通过客户端聊天时,后台(不管是tomcat 还是java)会根据 ChatSocket 类创建一个新的 chatSocket 对象,保存与用户的连接。因为chatService 是属于类的,所以也不是 null。
总结:
这里 websocket 的多对象机制和 spring 的 controller 注解机制,同时进行,互相没有矛盾。spring 会在初始化时创建一个没有意义的 ChatSocket 的单例对象,该对象在运行期间一直不会被使用,同时为 ChatSocket 的类进行了静态属性的完善,这是 spring 的唯一作用。
当有用户连接聊天时,java 会根据 ChatSocket 类进行创建对象,每个对象保持与对应的用户连接,因为类的静态属性已在启动时被 spring 初始化了,所以每个对象都可以正常使用。
安全性:
安全性要高于单例模式。单例模式全程使用一个对象,而 websocket 使用了多个对象,每个对象互相独立,属性互相分开,唯一的静态属性chatService,只是调用了其方法而已。如果内心实在害怕,自己根据实际情况在 chatService 中使用同步,或者加锁。