记 WebSocket实现消息推送所遇到的问题
首先springboot项目中引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
新建一个WebSocketConfig配置类
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
再写一个WebSocketServer类
@Component
@ServerEndpoint("/websocket/server")
public class WebSocketServer {
//两种获取spring容器中bean的方法,1.继承BeanFactoryPostProcessor;2.实现ApplicationContextAware类
private RedisMessageListenerContainer redisMessageListenerContainer = SpringUtils.getBean(RedisMessageListenerContainer.class);
private RedisMessageListenerContainer redisMessageListenerContainer1 = SpringU.getBean(RedisMessageListenerContainer.class);
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static AtomicInteger onlineCount=new AtomicInteger(0);
//concurrent包的线程安全Set,用来存放每个客户端对应的webSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
private SubscribeListener subscribeListener;
/**
* 连接建立成功调用的方法
* @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(Session session){
this.session = session;
webSocketSet.add(this); //加入set中
addOnlineCount(); //在线数加1
System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
subscribeListener = new SubscribeListener();
subscribeListener.setSession(session);
//设置订阅topic
redisMessageListenerContainer1.addMessageListener(subscribeListener, new ChannelTopic("LinJiaQi"));
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() throws IOException {
webSocketSet.remove(this); //从set中删除
subOnlineCount(); //在线数减1
redisMessageListenerContainer1.removeMessageListener(subscribeListener);
System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
* @param message 客户端发送过来的消息
* @param session 可选的参数
*/
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("来自客户端的消息:" + message);
//群发消息
for(WebSocketServer item: webSocketSet){
try {
item.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
continue;
}
}
}
/**
* 发生错误时调用
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error){
System.out.println("发生错误");
error.printStackTrace();
}
/**
* 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
* @param message
* @throws IOException
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
public int getOnlineCount() {
return onlineCount.get();
}
public void addOnlineCount() {
WebSocketServer.onlineCount.getAndIncrement();
}
public void subOnlineCount() {
WebSocketServer.onlineCount.getAndDecrement();
}
}
查了很多人写的文章,了解到了@ServerEndpoint 它的功能主要是将目前的类定义成一个WebSocket服务器端,每次请求都会创建一个实例。而为什么需要加
@Component注解呢?
可以看http://pomelolive.com/index.php/post/11.html这位老哥写的解释。
而在WebSocketServer类里是没办法自动注入spring容器中的bean的,因为websocket是每次链接都会创建一个新的对象,而spring对象都是以单例模式创建的
//两种获取spring容器中bean的方法,1.继承BeanFactoryPostProcessor;2.实现ApplicationContextAware类
private RedisMessageListenerContainer redisMessageListenerContainer = SpringUtils.getBean(RedisMessageListenerContainer.class);
private RedisMessageListenerContainer redisMessageListenerContainer1 = SpringU.getBean(RedisMessageListenerContainer.class);
所以可以用到两种方法去获取,我这里这个redisMessageListenerContainer类只是需要用到redis的推送订阅功能去实现用户直接的信息传递,所以只是做个演示。
一下是两种获取spring容器中bean的方法:
1.实现ApplicationContextAware接口
@Component
public class SpringU implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T>T getBean(Class<T> cls){
return context.getBean(cls);
}
}
2.继承BeanFactoryPostProcessor类
@Component
public final class SpringUtils implements BeanFactoryPostProcessor {
private static ConfigurableListableBeanFactory beanFactory; // Spring应用上下文环境
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
SpringUtils.beanFactory = beanFactory;
}
public static ConfigurableListableBeanFactory getBeanFactory() {
return beanFactory;
}
public static <T> T getBean(Class<T> clz) throws BeansException {
T result = (T) getBeanFactory().getBean(clz);
return result;
}
}
以上是我个人的理解,有错误希望各位大佬指正,代码都是参考别人写过的,自己去实验过的