springboot整合Websocket以及一种使用场景
一、websocket的执行流程
借用b站大佬的图讲解下:分三步走。
二、springboot整合websocket
1.导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> <version>2.4.4</version> </dependency>
2.初始化 ServerEndpointExporter
package com.yblue.chat.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; /** * @author JiaXinMa * @description 初始化 ServerEndpointExporter * 注意:如果使用独立的servlet容器,就不需要注入ServerEndpointExporter, * 因为servlet容器会自己提供和管理 * @date 2021/8/16 */ @Configuration public class WebsocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
3.配置ServerEndpoint的方法
package com.yblue.chat.config; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * @author JiaXinMa * @description Websocket的主要调用方法 * @date 2021/8/16 */ // ws://localhost:8080/websocket/test @Slf4j @ServerEndpoint("/websocket/test/{userId}") @Component public class WebsocketEndpoint { //注意;每次刷新页面(就是重新连接)session都会变 // 同时服务端会自动去调用关闭连接,然后再建立连接 private static Map<Object, Session> sessionMap = new ConcurrentHashMap<>(); //建立连接 @OnOpen public void onOpen(Session session,@PathParam("userId) Integer userId) { sessionMap.put(userId, session); log.info("建立连接成功!!!"); log.info("现有的连接数:" + sessionMap.size()); } //关闭连接 @OnClose public void onClose(Session session,@PathParam("userId") Integer userId) { try { sessionMap.remove(userId); session.close(); log.info("关闭连接成功!!!"); log.info("现有的连接数:" + sessionMap.size()); } catch (IOException e) { log.info("关闭连接失败!!!"); } } //群发消息 @OnMessage public void onMessage(String message) throws IOException { Collection<Session> sessions = sessionMap.values(); Iterator<Session> iterator = sessions.iterator(); while (iterator.hasNext()) { iterator.next().getBasicRemote().sendText(message); } log.info("实时通讯成功!!!"); } }
4.可能遇到的场景:
4.1 聊天室(我这个demo不适用,可以去修改,原理懂了都一样),建议可以采用 环信即时通讯 的技术
4.2 就是那些实时监控数据变化的的场景(比如我所在公司有个社区开门记录数据实时更新)
该场景逻辑是:人脸开门,然后后台有个echarts统计的表格实时变化
下面写个小demo测试下:
控制层代码:
package com.yblue.chat.controller; import com.yblue.chat.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * @author JiaXinMa * @description * @date 2021/8/16 */ @RestController public class UserController { @Autowired private UserService userService; //不用返回数据的,因为我在业务层调用WebsocketEndpoint的方法,他实时同步过去了 @GetMapping("/findById") public void findById(Integer userId) { userService.findById(userId); } //这里get请求主要是我想在地址栏测试而已,不要在意这些细节 @GetMapping("/update") public void update(Integer userId) { userService.update(userId); } }
业务层代码:
package com.yblue.chat.service; import com.yblue.chat.config.WebsocketEndpoint; import com.yblue.chat.mapper.UserMapper; import com.yblue.chat.pojo.User; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.websocket.Session; import java.io.IOException; /** * @author JiaXinMa * @description * @date 2021/8/16 */ @Service @Transactional @Slf4j public class UserService { @Autowired(required = false) UserMapper userMapper; @Autowired WebsocketEndpoint websocketEndpoint; public void findById(Integer userId) { try { User user = userMapper.selectById(userId); //这里调用,虽然可以在WebsocketEndpoint连接的时候调用 // 但是要注意bean的生命周期,需要特殊处理下,我菜暂时不会 //这里推送数据用json字符串,可以考虑用阿里巴巴的JSONObject这个类去转换 websocketEndpoint.onMessage(user.toString()); } catch (IOException e) { e.printStackTrace(); } } public void update(Integer userId) { try { User user = new User(); user.setUserId(userId); user.setName("我被改了"); userMapper.updateById(user); user = userMapper.selectById(userId); //这里调用也调用WebsocketEndpoint的推送数据方法 websocketEndpoint.onMessage(user.toString()); } catch (IOException e) { e.printStackTrace(); } } }
5.测试
我是综合这个网站(http://coolaf.com/tool/chattest)测试和地址栏测试的,效果如下
5.1创建连接
5.2查询数据
5.3修改数据
5.4关闭窗口刷新界面测试
如下效果,测试结果是会自动调用关闭连接方法(避免了只存不移除导致没有被垃圾回收,造成内存溢出),为了能够被回收,提高系统性能
这只是我对websocket比较浅薄的见解,如果有不对的地方,大佬帮忙指正。
想看更多精彩内容,可以关注我的CSDN