最近有一个需求,前台使用WebSocket请求后台,后台给其他页面推送数据。
有这样的需求,势必要在我编写的WebSocket层去调用我的Service层去访问数据库,于是我就写出了如下的代码:
@ServerEndpoint(value = "/websocket")
@Component
public class MyWebSocket {
private static ConcurrentHashMap<String, MyWebSocket> map = new ConcurrentHashMap<>();
private Session session;
@Autowired
private MyService myServiceImpl;
....
}
看起来一切都没有问题,但是当我运行的时候,我发现我的myServiceImpl是空!
结果去网上查了一下,发现在@ServerEndPoint中是无法使用@Autowired注入Bean的。
原理大概是这样:
其实在项目启动的时候,这个MyWebSocket已经被注入了这个MyService的Bean,但是WebSocket的特点就是每建立一个连接,会生成一个新的MyWebSocket对象,网上有人分析过源码,也可以理解成new了一个WebSocket,这样当然是不能获得自动注入的对象了。
所以我的解决方案大概就是获取到Spring容器,然后去进行手动注入。
因为新生成的MyWebSocket对象根本不存在与Spring容器中,所以传统的ContextLoader.getCurrentWebApplicationContext()自然也就无法使用。
我用到了ApplicationContextAware来在项目加载时就获得项目的Spring容器。代码如下
@Component
@Lazy(false)
public class MyApplicationContextAware implements ApplicationContextAware {
private static ApplicationContext APPLICATION_CONTEXT;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
APPLICATION_CONTEXT = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return APPLICATION_CONTEXT;
}
}
这样我们就可以在任何类中去拿到这个类的ApplicationContext
通过这个ApplicationContext我们就可以在新生成一个WebSocket对象时来手动注入Service层了
@ServerEndpoint(value = "/websocket")
@Component
public class MyWebSocket {
private static ConcurrentHashMap<String, MyWebSocket> map = new ConcurrentHashMap<>();
private Session session;
private MyService myServiceImpl = (MyService) MyApplicationContextAware.getApplicationContext().getBean("myServiceImpl");
这样问题就解决了!
版权声明:本文为qq_38182820原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。