起因:
在使用springboot写一个WebSocket通信的功能时,想要将RedisService 自动装配到代码中存储信息,于是使用@AutoWired注解进行自动装配,一运行,发现RedisTemplate对象报了NPE,百度一下得到这篇文章:SpringBoot 使用 @ServerEndpoint 后 @Autowired 失效问题分析和解决
@ServerEndpoint(value = "/test/one")
@Component
public class OneWebSocket {
// 这样注入会导致redisService报NPE错误
@Autowired
private RedisService RedisService;
}
得到了问题所在:
Spring管理采用单例模式(singleton),而 WebSocket 是多对象的,即每个客户端对应后台的一个 WebSocket 对象,也可以理解成 new 了一个 WebSocket,这样当然是不能获得自动注入的对象了,因为这两者刚好冲突。
@Autowired 注解注入对象操作是在启动时执行的,而不是在使用时,而 WebSocket 是只有连接使用时才实例化对象,且有多个连接就有多个对象。
所以我们可以得出结论,这个 Service 根本就没有注入到 WebSocket 当中。
接下来就是如何解决这个问题了,文章中提到了两种解决方案:
第一种:使用 static 静态对象
将需要注入的Bean改为静态,让它属于当前类,然后通过一个set方法进行注入即可解决。
举例:
@ServerEndpoint(value = "/test/one")
@Component
public class OneWebSocket {
// 将要注入的bean设置为static的,然后通过一个set方法注入
private static RedisService redisService;
@Autowired
public void setRedisService(RedisService redisService){
OneWebSocket.redisService = redisService;
}
}
第二种方法:通过上下文取出Bean对象
示例:
第一步,创建一个工具类,并实现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);
}
}
第二步:调用上面的工具类,通过需要注入的Bean的类名获取Bean对象
@ServerEndpoint(value = "/test/one")
@Component
public class OneWebSocket {
private RedisService redisService = SpringCtxUtils.getBean(RedisService.class);
}
不难看出,第一种方法比较简单