Spring Cloud/Boot WebSocket 無法注入其他類的解決辦法

SpringBoot官方文檔推薦實現的WebSocket的方式是添加@ServerEndpoint這個註解。我也是按照推薦實現的。
但是有問題。
我開始的寫的WebSocket的例子:

@ServerEndpoint(value = "/websocket" )
@Component
public class MyWebSocket
{   
    // 與某個客戶端的連接會話,需要通過它來給客戶端發送數據
    private Session session;

    @Autowired
    TestInfo testInfo;

    /**
     * 連接建立成功調用的方法
     */
    @OnOpen
    public void onOpen(Session session)
    {
        System.out.println(this.hashCode());
        this.session = session;     
        try
        {
            System.out.println(testInfo.name);
            sendMessage("新用戶添加進來了....");
        }
        catch (IOException e)
        {
            System.out.println("IO異常");
        }
    }

    /**
     * 連接關閉調用的方法
     */
    @OnClose
    public void onClose()
    {       
        System.out.println("有一連接關閉!當前在線人數爲" + getOnlineCount());
    }

    /**
     * 收到客戶端消息後調用的方法
     *
     * @param message
     *            客戶端發送過來的消息
     */
    @OnMessage
    public void onMessage(String message, Session session)
    {
        System.out.println("來自客戶端的消息:" + message);
    }

    /**
     * 發生錯誤時調用
     */
    @OnError
    public void onError(Session session, Throwable error)
    {
        System.out.println("發生錯誤");
        error.printStackTrace();
    }

    public void sendMessage(String message) throws IOException
    {
        this.session.getBasicRemote().sendText(message);        
    }
}

當客戶端發送請求的時候,會報空指針異常,TestInfo 爲空。
創建MyWebSocket,也是通過@Bean的形式實現的。其他的地方都沒有問題。

我已經autowired了,幹嘛沒注入啊。

TestInfo是通過Spring容器進行管理的,但是使用ServerEndpoint這個註解的時候,失效了
猜測原因就是這個MyWebSocket這個並不是Spring容器管理的。但是這個是官方推薦的實現方法 啊。

尋尋覓覓,最後在強大的stackoverflow中找到了解決問題的辦法。
https://stackoverflow.com/questions/30483094/springboot-serverendpoint-failed-to-find-the-root-webapplicationcontext

第一種方法:

繼續用ServerEndpoint。
定義一個MyEndpointConfigure

/**
 * 
 * @author lipengbin
 *
 */
public class MyEndpointConfigure extends ServerEndpointConfig.Configurator implements ApplicationContextAware
{
    private static volatile BeanFactory context;

    @Override
    public <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException
    {
         return context.getBean(clazz);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
    {
        System.out.println("auto load"+this.hashCode());
        MyEndpointConfigure.context = applicationContext;
    }
}

這個類的核心就是getEndpointInstance(Class clazz)這個方法。
定義了獲取類實例是通過ApplicationContext獲取。

@Configuration
public class MyConfigure
{

    @Bean
    public MyEndpointConfigure newConfigure()
    {
        return new MyEndpointConfigure();
    }
}

修改MyWebSocket的註解

@ServerEndpoint(value = “/websocket” )

@ServerEndpoint(value = “/websocket”,configurator=MyEndpointConfigure.class)
大致的意思可以理解了,創建類需要通過MyEndpointConfigure.getEndpointInstance()這個來實現。

運行一切正常。
但是這種形式並不是正常的Spring容器去正常去管理這個WebSocket,個人覺得並不是很好。

這個帖子同時還給出了第二解決方法。原生的Spring實現的WebSocket的辦法。

第二種解決辦法:

與其說是第二種辦法,不如說是Spring第二種實現WebSocket的方案。和第一種沒有任何的聯繫。
代碼如下:

核心Handler,有Spring的風格。

@Component
public class WsHandler extends TextWebSocketHandler
{

    @Autowired  
    TestInfo testInfo;

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception
    {
        super.afterConnectionClosed(session, status);
        System.out.println("close....");
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception
    {
        super.afterConnectionEstablished(session);
        System.out.println("----->"+testInfo.test());
        System.out.println("建立新的會話");
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception
    {       
        System.out.println(message.getPayload());
        TextMessage msg=new TextMessage(message.getPayload());
        session.sendMessage(msg);

    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception
    {
        super.handleMessage(session, message);
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception
    {
        super.handleTransportError(session, exception);
    }

}

簡單實現幾個關鍵的方法。

TestInfo 直接注入。

編寫Configure類

@Configuration
@EnableWebSocket
public class WsConfigure implements WebSocketConfigurer
{
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry)
    {
        System.out.println("==========================");
        registry.addHandler(myHandler(), "/websocket").setAllowedOrigins("*");
    }

    @Bean
    public WsHandler myHandler()
    {
        return new WsHandler();
    }
}

這種實現方法可以查看官方文檔。
https://docs.spring.io/spring/docs/4.3.13.RELEASE/spring-framework-reference/htmlsingle/#websocket

經測試可以正常工作。
個人建議採用第二種方法,因爲這個是Spring自帶的WebSocket的實現方式。

實現上需要花時間看看Spring是如何將WebSocket的請求進行處理。以後有時間補上這個。

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