请选择 进入手机版 | 继续访问电脑版
 找回密码
 立即注册

QQ登录

只需一步,快速开始

KevinChen 讲师达人认证 悬赏达人认证 SpreadJS 开发认证
论坛元老   /  发表于:2020-9-30 11:49  /   查看:2456  /  回复:0
前言:
版主在日常沟通、技术社区等与使用SpreadJS的小伙伴们交流的过程中,遇到了很多前后端结合的问题。其实从定位来看,SpreadJS无论从技术架构,还是产品设计层面,都是纯前端的控件。但它毕竟最终还是要结合后端环境来开发部署和应用的,所以我有了写这篇文章的想法。
本文利用了一个比较贴近用户的前后端结合的示例工程,来给大家演示一下SpreadJS的一些使用场景和实现技巧,希望能够帮助大家打开思路,能够更好地解决业务问题,提升用户体验。
由于涉及的功能点较多,代码量也不小,这个主题我会分为三篇文章来发布,本篇文章是这个系列的第二篇。第一篇请移步:《SpreadJS结合Springboot实现跨文档数据公式关联 _ Part1
实现思路:
本文是系列文章第二篇,主要讲解如何实现多屏协同数据关联(注意不是协同编辑),本例采用了SpringBootWebSocket来实现页面终端的实时关联。
运行环境:
Java SpringBoot+ SpreadJS
运行方法:
解压后导入Eclipse(或其它IDE工具),Run Java Application ,访问localhost:8080即可。
关键代码讲解:
关联文章《SpreadJS结合Springboot实现跨文档数据公式关联 _ Part1》和《SpreadJS结合Springboot实现跨文档数据公式关联 _ Part3》。
本篇主要讲解前端JavaScript结合Java端如何建立Websocket服务,实现消息推送服务。如果对这方面比较精通的小伙伴,可以忽略,直接跳到Part3
Java实现部分:
  1. // Socket管理器:用来管理WebSocket连接,通知后台连接状态;
  2. /**
  3. * socket管理器
  4. */
  5. public class SocketManager {
  6.     private static ConcurrentHashMap<String, WebSocketSession> manager = new ConcurrentHashMap<String, WebSocketSession>();

  7.     public static void add(String key, WebSocketSession webSocketSession) {
  8.         System.out.println("新添加webSocket连接 {"+key+"} ");
  9.         manager.put(key, webSocketSession);
  10.     }

  11.     public static void remove(String key) {
  12.             System.out.println("移除webSocket连接 {"+key+"} ");
  13.         manager.remove(key);
  14.     }

  15.     public static WebSocketSession get(String key) {
  16.             System.out.println("获取webSocket连接 {"+key+"}");
  17.         return manager.get(key);
  18.     }
  19.    
  20. }

  21. // WebSocket配置类,用来设置点对点、广播的地址和参数:
  22. /**
  23. * WebSocketConfig配置
  24. */
  25. @SuppressWarnings("deprecation")
  26. @Configuration
  27. @EnableWebSocketMessageBroker
  28. public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

  29.     @Autowired
  30.     private WebSocketDecoratorFactory webSocketDecoratorFactory;
  31.     @Autowired
  32.     private PrincipalHandshakeHandler principalHandshakeHandler;

  33.     @Override
  34.     public void registerStompEndpoints(StompEndpointRegistry registry) {
  35.         /**
  36.          * myUrl
  37.          */
  38.         registry.addEndpoint("/myUrl")
  39.                 .setAllowedOrigins("*")
  40.                 .setHandshakeHandler(principalHandshakeHandler)
  41.                 .withSockJS();
  42.     }

  43.     @Override
  44.     public void configureMessageBroker(MessageBrokerRegistry registry) {
  45.         /**
  46.          * queue 点对点
  47.          * topic 广播
  48.          * user 点对点前缀
  49.          */
  50.         registry.enableSimpleBroker("/queue", "/topic");
  51.         registry.setUserDestinationPrefix("/user");
  52.     }

  53.     @Override
  54.     public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
  55.         registration.addDecoratorFactory(webSocketDecoratorFactory);
  56.         super.configureWebSocketTransport(registration);
  57.     }
  58. }

  59. // 装饰类,扩展了握手挥手的功能,可以在这里加入一些自定义逻辑
  60. /**
  61. * 服务端和客户端在进行握手挥手时会被执行
  62. */
  63. @Component
  64. public class WebSocketDecoratorFactory implements WebSocketHandlerDecoratorFactory {
  65.     @Override
  66.     public WebSocketHandler decorate(WebSocketHandler handler) {
  67.         return new WebSocketHandlerDecorator(handler) {
  68.             @Override
  69.             public void afterConnectionEstablished(WebSocketSession session) throws Exception {
  70.                 System.out.println("建立连接  sessionId = {"+session.getId()+"}");
  71.                 Principal principal = session.getPrincipal();
  72.                 if (principal != null) {
  73.                         System.out.println("key = {"+principal.getName()+"} 存入");
  74.                     //Test 身份校验成功,缓存socket连接
  75.                     SocketManager.add(principal.getName(), session);
  76.                 }
  77.        

  78.                 super.afterConnectionEstablished(session);
  79.             }

  80.             @Override
  81.             public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
  82.                     System.out.println("退出连接  sessionId = {"+session.getId()+"}");
  83.                 Principal principal = session.getPrincipal();
  84.                 if (principal != null) {
  85.                     //Test 身份校验成功,移除socket连接
  86.                     SocketManager.remove(principal.getName());
  87.                 }
  88.                 super.afterConnectionClosed(session, closeStatus);
  89.             }
  90.         };
  91.     }
  92. }

  93. // 身份管理
  94. /**
  95. *  Principal
  96. */
  97. @Component
  98. public class PrincipalHandshakeHandler extends DefaultHandshakeHandler {
  99.     @Override
  100.     protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
  101.         if (request instanceof ServletServerHttpRequest) {
  102.             ServletServerHttpRequest servletServerHttpRequest = (ServletServerHttpRequest) request;
  103.             HttpServletRequest httpRequest = servletServerHttpRequest.getServletRequest();
  104.             final String token = httpRequest.getParameter("token");
  105.             if (StringUtils.isEmpty(token)) {
  106.                 return null;
  107.             }
  108.             return new Principal() {
  109.                 @Override
  110.                 public String getName() {
  111.                     return token;
  112.                 }
  113.             };
  114.         }
  115.         return null;
  116.     }
  117. }

  118. // 控制器:只展示了一个方法的实现,本例中只有点对点消息的场景,如果用到广播场景,请参考完整示例
  119.     /**
  120.              * 点对点发消息,这边需要注意,由于前端传过来json数据,所以使用@RequestBody
  121.              * 这边需要前端开通var socket = new SockJS(host+'/myUrl' + '?token=4567');   token为指定name
  122.      * @param map
  123.      */
  124.     @MessageMapping("/sendToWorkbook")
  125.     @SendToUser("/queue/sendToWorkbook")
  126.     public void sendMyUser(@RequestBody Map<String, String> map) {
  127.         System.out.println("map = {"+map+"}");
  128.         WebSocketSession webSocketSession = SocketManager.get(map.get("referFile"));
  129.         if (webSocketSession != null) {
  130.                 System.out.println("sessionId = {"+webSocketSession.getId()+"}");
  131.             template.convertAndSendToUser(map.get("referFile"), "/queue/sendUser", map.get("data"));
  132.         }
  133. }
复制代码

前端JavaScript实现部分:

  1. /*****************WebSocket*********************/

  2. var stompClient = null;
  3. var host="http://localhost:8080";

  4. function setConnected(connected) {
  5.     //console.log(connected?"connected":"disConnected");
  6. }

  7. function connect(fileName) {
  8.     var socket = new SockJS(host+'/myUrl' + '?token='+fileName);
  9.     stompClient = Stomp.over(socket);
  10.     stompClient.connect({}, function(frame) {
  11.         setConnected(true);
  12.         //console.log('Connected:' + frame);
  13.         stompClient.subscribe('/user/queue/sendUser', function(response) {
  14.                         //alert(1);
  15.             showResponse(response.body);
  16.         });
  17.     });
  18. }
  19. function disconnect() {
  20.     if (stompClient != null) {
  21.         stompClient.disconnect();
  22.     }
  23.     setConnected(false);
  24. }
  25. function send(referFile, data, isValued) {
  26.         //stompClient.send("/sendToWorkbook", {}, JSON.stringify({isValued:isValued, message:message}));
  27.         data.isValued = isValued;
  28.         stompClient.send("/sendToWorkbook", {}, JSON.stringify({referFile: referFile, data: JSON.stringify(data) }));
  29. }
  30. function showResponse(response) {
  31.         console.log(response);
  32.         // showResponse用来作为响应消息的入口,帮我们处理得到消息后的逻辑。这部分请参考part3
  33. }

  34. /*****************WebSocket End*********************/
复制代码



SpreadJS_SpringBoot.zip

6.34 MB, 下载次数: 28

0 个回复

您需要登录后才可以回帖 登录 | 立即注册
返回顶部