• Springboot2.7集成websocket及相关问题


    1、集成websocket完整代码

    导入maven依赖

    1. <dependency>
    2. <groupId>org.springframework.bootgroupId>
    3. <artifactId>spring-boot-starter-websocketartifactId>
    4. dependency>

    服务端代码

    1. 1)注入bean
    2. @Configuration
    3. @EnableWebSocket
    4. public class WebSocketConfig {
    5. @Bean
    6. public ServerEndpointExporter serverEndpointExporter() {
    7. return new ServerEndpointExporter();
    8. }
    9. }
    10. 2)服务端开放的服务及其业务逻辑
    11. @Slf4j
    12. @Component
    13. @ServerEndpoint("/testcase/page/{clientId}")
    14. //@Lazy
    15. public class WsMessageService {
    16. //与某个客户端的连接会话
    17. private Session session;
    18. //存放每个客户端对应的WebSocket对象。
    19. private static Map webSocketsBeanMap = new ConcurrentHashMap<>();
    20. //每次连接都是一个新的会话对象,线程安全的
    21. String userId;
    22. //注入业务类。要注意setTestcaseService()的名称不能错,set后的字符串要和要注入的变量名一致。
    23. private static TestcaseService testcaseService;
    24. @Autowired
    25. public void setTestcaseService(TestcaseService testcaseService) {
    26. WsMessageService.testcaseService= testcaseService;
    27. }
    28. //private TestcaseService testcaseService = SpringContextUtil.getBean(TestcaseService.class);
    29. @OnOpen
    30. public void onOpen(Session session, @PathParam(value = "clientId") String userId) {
    31. this.session = session;
    32. this.userId = userId;
    33. webSocketsBeanMap.put(userId, this);
    34. log.info("OnOpen连接成功,userId:{},当前在线人数:{}", userId, this.getOnLineCount());
    35. }
    36. @OnMessage
    37. public void onMessage(String message) throws IOException {
    38. Session session = webSocketsBeanMap.get(this.userId).session;
    39. if (session==null || !session.isOpen()) {
    40. return;
    41. }
    42. log.info("收到客户端的消息:" + message);
    43. //start业务
    44. Integer pageNum = null;
    45. Integer pageSize = null;
    46. String pid = null;
    47. String vid = null;
    48. if (session.getRequestParameterMap().get("pageNum")!= null) {
    49. pageNum = Integer.valueOf(session.getRequestParameterMap().get("pageNum").get(0));
    50. }
    51. if (session.getRequestParameterMap().get("pageNum")!= null) {
    52. pageSize = Integer.valueOf(session.getRequestParameterMap().get("pageSize").get(0));
    53. }
    54. if (session.getRequestParameterMap().get("pid")!= null) {
    55. pid = session.getRequestParameterMap().get("pid").get(0);
    56. }
    57. if (session.getRequestParameterMap().get("vid")!= null) {
    58. vid = session.getRequestParameterMap().get("vid").get(0);
    59. }
    60. PageInfo pageInfo = testcaseService.findTestcaseByMultiCondition(pageNum, pageSize, pid, vid);
    61. //end业务
    62. try {
    63. JSONObject object = new JSONObject();
    64. object.put("data", pageInfo);
    65. object.put("code", 200);
    66. object.put("message", "SUCCESS");
    67. this.session.getBasicRemote().sendText(String.valueOf(object.toString()));
    68. } catch (IOException e) {
    69. throw new RuntimeException(e);
    70. }
    71. }
    72. @OnClose
    73. public void onClose() throws IOException {
    74. log.info("会话关闭,关闭会话的用户Id为:{}", this.userId);
    75. webSocketsBeanMap.remove(this.userId);
    76. log.info("当前在线人数:{}", this.getOnLineCount());
    77. }
    78. @OnError
    79. public void onError(Session session, Throwable error) {
    80. log.error("连接错误:" + error.getMessage());
    81. error.printStackTrace();
    82. }
    83. private int getOnLineCount() {
    84. return webSocketsBeanMap.size();
    85. }
    86. }

    测试服务端的可用性。在浏览器的控制台输入以下代码可以测试。

    1. function initWebSocket(wsUri) {
    2. var websocket = new WebSocket(wsUri);
    3. websocket.onopen = function(evt) {
    4. console.log('连接建立中... '+wsUri);
    5. };
    6. websocket.onclose = function(evt) {
    7. console.log('连接关闭中...', evt);
    8. };
    9. websocket.onmessage = function(evt) {
    10. console.log('收到来自服务端的消息:', evt.data);
    11. };
    12. websocket.onerror = function(evt) {
    13. console.log('发生错误...', evt);
    14. };
    15. return websocket;
    16. }
    17. var websocket = initWebSocket("ws://ip:port/testcase/page/123?pid=5922cc3d03f74012a7112c931c8497d7&vid=fc2d7a6049ff4c7bafc40bf659b4e903");
    18. var msg, i = 0;
    19. var loop = setInterval(function(){
    20. msg = "Hello " +(i++);
    21. if(websocket.readyState == WebSocket.OPEN) {
    22. websocket.send(msg);
    23. console.log('已发送消息:' +msg);
    24. } else{
    25. clearInterval(loop);
    26. console.log('连接已关闭');
    27. }
    28. }, 10000);

    2、集成websocket另一种方法(用控制台测试时返回的数据会乱码)

    代码找不到了。但那种方法有一个特征就是使用的注解比当前的方法少。

    3、解决业务类注入不进去的问题(两种方法,推荐第一种)

    (1)第一种:就是当前使用的注入方法。注意命名,否则不会注入成功

    1. //注入业务类。要注意setTestcaseService()的名称不能错,set后的字符串要和要注入的变量名一致。
    2. private static TestcaseService testcaseService;
    3. @Autowired
    4. public void setTestcaseService(TestcaseService testcaseService) {
    5. WsMessageService.testcaseService= testcaseService;
    6. }

    (2)第二种:自己写一个SpringContextUtil工具类,协助注入

    1. package cn.xxxxx.tmMaster.utils;
    2. import lombok.extern.slf4j.Slf4j;
    3. import org.mybatis.logging.LoggerFactory;
    4. import org.springframework.beans.BeansException;
    5. import org.springframework.context.ApplicationContext;
    6. import org.springframework.context.ApplicationContextAware;
    7. import org.springframework.stereotype.Component;
    8. import java.util.logging.Logger;
    9. @Slf4j
    10. @Component
    11. public class SpringContextUtil implements ApplicationContextAware {
    12. private static ApplicationContext applicationContext;
    13. @Override
    14. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    15. SpringContextUtil.applicationContext = applicationContext;
    16. }
    17. /**
    18. * 获取 applicationContext
    19. */
    20. public static ApplicationContext getApplicationContext() {
    21. return applicationContext;
    22. }
    23. /**
    24. * 通过 name 获取 bean 对象
    25. */
    26. public static Object getBean(String name) {
    27. return getApplicationContext().getBean(name);
    28. }
    29. /**
    30. * 通过 class 获取 bean 对象
    31. */
    32. public static T getBean(Class clazz) {
    33. return getApplicationContext().getBean(clazz);
    34. }
    35. /**
    36. * 通过 name,clazz 获取指定的 bean 对象
    37. */
    38. public static T getBean(String name, Class clazz) {
    39. return getApplicationContext().getBean(name, clazz);
    40. }
    41. }

    在WsMessageService中使用如下代码注入进去,但在类上必须要@Lazy进行懒加载,否则会报错

    1. @Lazy
    2. public class WsMessageService {
    3. ……
    4. private TestcaseService testcaseService = SpringContextUtil.getBean(TestcaseService.class);
    5. ……
    6. }

    不加@Lazy报错如下:org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'wsMessageService' defined in file [D:\javaworkspace\test-xxxx\tm-master\target\classes\cn\xxxxx\tmMaster\service\testcase\WsMessageService.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [cn.xxxxx.tmMaster.service.testcase.WsMessageService]: Constructor threw exception; nested exception is java.lang.NullPointerException

    1. org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'wsMessageService' defined in file [D:\javaworkspace\test-xxxx\tm-master\target\classes\cn\xxxxx\tmMaster\service\testcase\WsMessageService.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [cn.xxxxx.tmMaster.service.testcase.WsMessageService]: Constructor threw exception; nested exception is java.lang.NullPointerException
    2. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1334) ~[spring-beans-5.3.29.jar:5.3.29]
    3. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232) ~[spring-beans-5.3.29.jar:5.3.29]
    4. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[spring-beans-5.3.29.jar:5.3.29]
    5. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.29.jar:5.3.29]
    6. at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.29.jar:5.3.29]
    7. at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.29.jar:5.3.29]
    8. at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.29.jar:5.3.29]
    9. at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.29.jar:5.3.29]
    10. at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) ~[spring-beans-5.3.29.jar:5.3.29]
    11. at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:921) ~[spring-context-5.3.29.jar:5.3.29]
    12. at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.29.jar:5.3.29]
    13. at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.14.jar:2.7.14]
    14. at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) [spring-boot-2.7.14.jar:2.7.14]
    15. at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) [spring-boot-2.7.14.jar:2.7.14]
    16. at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) [spring-boot-2.7.14.jar:2.7.14]
    17. at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) [spring-boot-2.7.14.jar:2.7.14]
    18. at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) [spring-boot-2.7.14.jar:2.7.14]
    19. at cn.xxxxx.tmMaster.MasterApplication.main(MasterApplication.java:17) [classes/:na]
    20. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_382]
    21. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_382]
    22. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_382]
    23. at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_382]
    24. at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) [spring-boot-devtools-2.7.14.jar:2.7.14]
    25. Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [cn.xxxxx.tmMaster.service.testcase.WsMessageService]: Constructor threw exception; nested exception is java.lang.NullPointerException
    26. at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:224) ~[spring-beans-5.3.29.jar:5.3.29]
    27. at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:87) ~[spring-beans-5.3.29.jar:5.3.29]
    28. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1326) ~[spring-beans-5.3.29.jar:5.3.29]
    29. ... 22 common frames omitted
    30. Caused by: java.lang.NullPointerException: null
    31. at cn.xxxxx.tmMaster.utils.SpringContextUtil.getBean(SpringContextUtil.java:55) ~[classes/:na]
    32. at cn.xxxxx.tmMaster.service.testcase.WsMessageService.(WsMessageService.java:48) ~[classes/:na]
    33. at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_382]
    34. at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_382]
    35. at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_382]
    36. at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_382]
    37. at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:211) ~[spring-beans-5.3.29.jar:5.3.29]
    38. ... 24 common frames omitted

    参考资料:

    SpringBoot集成WebSocket实现客户端与服务端长连接通信_springboot实现websocket客户端_拄杖忙学轻声码的博客-CSDN博客 SpringBoot集成WebSocket实现客户端与服务端长连接通信

    快速搭建springboot websocket客户端_springboot实现websocket客户端_wcybaonier的博客-CSDN博客 快速搭建springboot websocket客户端

  • 相关阅读:
    vue el-select搜索功能--区分输入内容后得到的是否是下拉框选择数据
    PD3.1详解 第二章(EPR)
    美芯片禁令再次扩大,波及英伟达、AMD以及intel等科技公司 | 百能云芯
    基于python+Django的二维码生成算法设计与实现
    人工智能开源社区论坛----开源助力多领域AI生态发展| ChinaOSC
    写给小白的开源编译器
    【Spring Cloud】 Gateway配置说明示例
    一心为农,以智取胜——张继群的智慧农业之路
    编译器是如何将芯片执行的第一个指令放到芯片起始地址的?
    3396: [Usaco2009 Jan]Total flow 水流 (最大流)
  • 原文地址:https://blog.csdn.net/qq_30719815/article/details/132907229