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的请求进行处理。以后有时间补上这个。