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的請求進行處理。以後有時間補上這個。