解決Springboot的WebSocket組件(即@ServerEndPoint)無法@Autowired的問題

最近有一個需求,前臺使用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");

 

這樣問題就解決了!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章