文章目录
今日内容
-
⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼
① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨
学习目标
-
⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽
⑾ ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇
① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨
一、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>2.7.0</version>
</dependency>
二、核心配置
@Configuration
public class WebSocketConfiguration {
/**
* 开启@ServerEndpoint注解声明, WebSocketEndpoint
*
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
三、创建WebSocket工具类
注意: 在
@ServerEndpoint
修饰的类中, 无法直接通过@Autowired
注入service或者bean原因及解决方法: 查看
SpringBoot使用@ServerEndpoint无法@Autowired依赖注入问题解决
@Slf4j
@Component
@ServerEndpoint("/inquire/modelSession/{qccUserId}")
public class ModelSessionWebSocket {
/**
* 存储Session集合
*/
private static ConcurrentHashMap<String, Session> sessionMap = new ConcurrentHashMap<>();
/**
* 存储对象集合
*/
private static ConcurrentHashMap<String, SysUser> userMap = new ConcurrentHashMap<>();
/**
* 连接建立时的操作
*
* @param session
* @param qccUserId
*/
@OnOpen
public void onOpen(Session session, @PathParam("qccUserId") String qccUserId) throws Exception {
// 判断该用户是否已经连接
if (sessionMap.get(qccUserId) != null) {
log.error("用户已经登录! {}", qccUserId);
return;
}
// 判断是否有该用户
ISysUserService sysUserService = SpringUtils.getBean(ISysUserService.class);
SysUser sysUser = sysUserService.lambdaQuery().eq(SysUser::getQccUserId, qccUserId).one();
if (sysUser == null) {
log.error("[ModelSessionWebSocket] 提醒您: 未获取到用户信息! {}", qccUserId);
session.close();
return;
}
// 设置Map
setMap(session, sysUser);
// 获取在线人数
log.info("用户连接: {}, 名称为: {}, 当前在线人数: {}", qccUserId, sysUser.getNickName(), sessionMap.size());
}
/**
* 设置Map集合
*
* @param session 会话
* @param sysUser 用户
*/
private void setMap(Session session, SysUser sysUser) {
// 获取用户Id
String qccUserId = sysUser.getQccUserId();
// 存储会话到会话集合
sessionMap.put(qccUserId, session);
// 存储用户到用户集合
userMap.put(qccUserId, sysUser);
}
/**
* 连接关闭时操作
*
* @param session 会话
*/
@OnClose
public void onClose(Session session) {
String qccUserId = getQccUserIdBySession(session);
removeMap(session);
log.info("用户下线: {}, 当前在线人数: {}", qccUserId, sessionMap.size());
}
/**
* 当用户关闭会话时, 移除用户会话记录
*
* @param session 会话
*/
private void removeMap(Session session) {
String qccUserId = getQccUserIdBySession(session);
if (StrUtil.isEmpty(qccUserId)){
// 如果用户不存在, 则直接结束
return;
}
sessionMap.remove(qccUserId);
userMap.remove(qccUserId);
}
/**
* 根据会话找到用户id
*
* @param session 会话
* @return
*/
private String getQccUserIdBySession(Session session) {
for (String qccUserId : sessionMap.keySet()) {
if (sessionMap.get(qccUserId).getId().equals(session.getId())){
return qccUserId;
}
}
return null;
}
/**
* 连接异常时操作
*
* @param throwable
*/
@OnError
public void onError(Throwable throwable) {
log.error("连接异常: {}", throwable.getMessage());
throwable.printStackTrace();
}
/**
* 接收到消息时的操作
*
* @param session
* @param message
*/
@OnMessage
public void onMessage(Session session, String message) {
// 根据会话获取当前用户
SysUser sysUser = getUserBySession(session);
if (ObjectUtil.isNull(sysUser)) {
log.info("未获取到用户信息!");
return;
}
// 解析消息
log.info("用户: {}, 接收到了消息: {}", sysUser.getQccUserId(), message);
if (!JSONUtil.isTypeJSON(message)) {
log.info("消息体异常: {}", message);
return;
}
ModelSessionDto modelSessionDto = JSONUtil.toBean(message, ModelSessionDto.class);
Session toSession = sessionMap.get(sysUser.getQccUserId());
if (toSession == null){
log.info("用户: {}不在线!", sysUser.getQccUserId());
return;
}
// 发送消息
// ModelSessionService modelSessionService = SpringUtils.getBean(ModelSessionService.class);
// modelSessionService.conversation(modelSessionDto, session, sysUser);
toSession.getBasicRemote().sendText("收到! 收到! 收到!");
}
/**
* 根据会话找到用户
*
* @param session 会话
* @return
*/
private SysUser getUserBySession(Session session) {
String qccUserId = getQccUserIdBySession(session);
if (StrUtil.isEmpty(qccUserId)) {
return null;
}
return userMap.get(qccUserId);
}
}
四、以上即可使用, 具体的业务需要自行修改
@ServerEndpoint无法@Autowired依赖注入问题解决
1.问题
websocket里面使用@Autowired注入service或bean时,报空指针异常。
@Component
@ServerEndpoint("/groupChat/{groupNo}/{uuid}")
public class GroupChatWebsocket {
@Autowired
private RedisTemplateUtil redisTemplateUtil;
}
2.解决方案
2.1 使用static静态对象
private static RedisTemplateUtil redisUtil;
@Autowired
public static void setRedisTemplateUtil(RedisTemplateUtil redisUtil) {
WebSocketServer.redisUtil = redisUtil;
}
2.2 动态的从spring容器中取出RedisTemplateUtil 推荐方案
package cn.pconline.framework.util;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* 获取spring容器
* 当一个类实现了这个接口ApplicationContextAware之后,这个类就可以方便获得ApplicationContext中的所有bean。
* 换句话说,这个类可以直接获取spring配置文件中所有有引用到的bean对象
* 前提条件需作为一个普通的bean在spring的配置文件中进行注册
*/
public class SpringCtxUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringCtxUtils.applicationContext = applicationContext;
}
public static <T> T getBean(Class<T> type) {
try {
return applicationContext.getBean(type);
} catch (NoUniqueBeanDefinitionException e) { // 出现多个,选第一个
String beanName = applicationContext.getBeanNamesForType(type)[0];
return applicationContext.getBean(beanName, type);
}
}
public static <T> T getBean(String beanName, Class<T> type) {
return applicationContext.getBean(beanName, type);
}
}