Spring 集成 WebSocket

  WebSocket 协议主要用来实现后台服务端主动向客户端推送消息,当然,协议本身支持双向通信。

   在 Spring 项目中集成 WebSocket 还是比较简单的,但是 WebSocket 的 Session 和 HttpSession 并不相通,如果想获取 HttpSession 中的登录用户就要稍费工夫,因此,记录成篇。


一、集成 WebSocket

  1. 项目添加依赖包:
    1
    2
    3
    4
    5
    <!-- SpringBoot Websocket -->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
  2. 增加 WebSocketConfig 配置类:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /**
    * websocket 配置
    *
    * @author xhp
    */
    @Configuration
    public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
    return new ServerEndpointExporter();
    }

    }
  3. 编写 WebSocketSever 类:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    @Component
    @ServerEndpoint(value = "/ws/message")
    public class WebSocketServer {

    /**
    * 连接建立成功调用的方法
    */
    @OnOpen
    public void onOpen(Session session) throws Exception {

    }

    /**
    * 连接关闭时处理
    */
    @OnClose
    public void onClose(Session session) {

    }

    /**
    * 抛出异常时处理
    */
    @OnError
    public void onError(Session session, Throwable exception) throws Exception {
    if (session.isOpen()) {
    // 关闭连接
    session.close();
    }
    }

    /**
    * 服务器接收到客户端消息时调用的方法
    */
    @OnMessage
    public void onMessage(String message, Session session) {

    }

    }
  4. 编写 html 页面,测试连接:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    <script type="text/javascript">
    $(document).ready(function(){
    // WebSocket
    var wsBaseUrl = /*[[${"ws://" + #httpServletRequest.getServerName() + ":" + #httpServletRequest.getServerPort() + #httpServletRequest.getContextPath()}]]*/'';
    var ws = new WebSocket(wsBaseUrl + '/ws/message');
    ws.onopen = function (event) {
    console.log('WS 已打开连接 ');
    }
    ws.onmessage = function (event) {
    // TODO 接收消息
    console.log(event.data);
    }
    ws.onclose = function (event) {
    console.log('WS 已关闭连接 ');
    }

    // 发送消息
    $('#btn_send').click(function() {
    var message = $('#message').val();
    if (ws) {
    ws.send(message);
    } else {
    alert(" 未连接到服务器 ");
    }
    });
    // 断开
    $('#btn_exit').click(function() {
    if (ws) {
    ws.close();
    ws = null;
    }
    });
    })
    </script>

二、WebSocket 会话获得登录用户

  1. 场景说明:

    • 项目中常见场景,需要给某个用户或者某类用户推送消息;
    • 我们需要在全部的 WebSocket 连接会话里根据登录用户匹配有效的发送会话;
    • 因为 WebSocket 的 Session 和 HttpSession 是相互独立的,本身没用对应关系,那么,我们只能想办法把需要的信息放到 Session 里。
  2. 修改 WebSocketConfig 配置类,重写其继承类的方法,将用户信息放入,示例如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.socket.server.standard.ServerEndpointExporter;

    import javax.websocket.HandshakeResponse;
    import javax.websocket.server.HandshakeRequest;
    import javax.websocket.server.ServerEndpointConfig;

    /**
    * websocket 配置
    *
    * @author xhp
    */
    @Configuration
    public class WebSocketConfig extends ServerEndpointConfig.Configurator {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
    return new ServerEndpointExporter();
    }

    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
    // 将用户信息存储到 socket 的配置里
    sec.getUserProperties().put("sysUser", ShiroUtils.getSysUser());
    super.modifyHandshake(sec, request, response);
    }
    }
  3. 修改 WebSocketServer 类,读取用户信息,示例如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    /**
    * websocket 消息处理
    *
    * @author xhp
    */
    @Component
    @ServerEndpoint(value = "/ws/message", configurator = WebSocketConfig.class)
    public class WebSocketServer {

    /**
    * 连接建立成功调用的方法
    */
    @OnOpen
    public void onOpen(Session session) throws Exception {
    // 读取用户信息
    SysUser sysUser= (SysUser)session.getUserProperties().get("sysUser");

    }
    }

三、注意事项

  1. 本示例项目是 Springboot,如果项目是 Spring 而非 Springboot,那么第一步的依赖包有所不同,后面的步骤是一致的;
  2. WebSocketServer 类的类注解需要增加对配置文件的引入 configurator = WebSocketConfig.class,否则配置文件的重写方法是不生效的;