起因:

在使用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);

}

不难看出,第一种方法比较简单