SpringBoot对WebSocket提供非常友好的支持,很方便在项目中快速集成WebSocket功能,实现群聊或者单聊功能。


准备内容:

1、添加 WebSocket依赖及 相关js库的 webjars 依赖,参考《@ServerEndpoint 开发聊天室功能》;

2、HTML页面JS代码及控制类;

3、实现 WebSocketMessageBrokerConfigurer 接口的 WebSocketConfig配置类;

4、消息实体类 SendMsgEO;



一、WebSocketConfig配置类:



package com.mall.websocket;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker // 开启WebSocket代理
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override
   public void configureMessageBroker(MessageBrokerRegistry config) {
      // 设置消息代理前缀
      // 即如果消息的前缀是 /topic ,就会将消息转发给消息代理(broker),
      // 再由消息代理将消息广播给当前连接的客户端。
      config.enableSimpleBroker("/topic");

      // 表示配置一个或多个前缀,通过这些前缀过滤出需要被注解方法处理的消息。
      // 例如,前缀为 /app 的 destination 可以通过@MessageMapping注解的方法处理,
      // 而其他 destination (例如 /topic /queue)将被直接交给 broker 处理
      config.setApplicationDestinationPrefixes("/app");
   }
   @Override
   public void registerStompEndpoints(StompEndpointRegistry registry) {

      // 表示定义一个前缀为 /chat 的 endPoint,并开启 sockjs 支持,
      // sockjs 可以解决浏览器对 WebSocket 的兼容性问题,
      // 客户端将通过这里配置的 URL 来建立 WebSocket 连接
      registry.addEndpoint("/chat").withSockJS();
   }
}



Spring 框架提供了基于 WebSocket 的 STOMP 支持,STOMP 是一个简单的可互操作的协议,通常被用于通过中间服务器在客户端之间进行异步消息传递。

  • 自定义类 WebSocketConfig 继承自 WebSocketMessageBrokerConfigurer 进行 WebSocket 配置,然后通过@EnableWebSocketMessageBroker 注解开启 WebSocket 消息代理。
  • config.enableSimpleBroker("/topic") 表示设置消息代理的前缀,即如果消息的前缀是"/topic",就会将消息转发给消息代理 (broker),再由消息代理将消息广播给当前连接的客户端。
  • config.setApplicationDestinationPrefixes("/app") 表示配置一个或多个前缀,通过这些前缀过滤出需要被注解方法处理的消息。例如,前缀为"/app"的 destination 可以通过@MessageMapping注解的方法处理,而其他 destination (例如"/topic" "/queue") 将被直接交给 broker 处理。
  • registry.addEndpoint("/chat").withSockJS() 则表示定义一个前缀为 "/chat" 的 endPoint,并开启 sockjs 支持,sockjs 可以解决浏览器对 WebSocket 的兼容性问题,客户端将通过这里配置的URL 来建立 WebSocket 连接。




二、Controller控制类和消息实体类 SendMsgEO



package com.mall.controller;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import com.mall.websocket.SendMsgEO;
@Controller
public class ChatRoomController {

   @GetMapping("/chatroom")
   public String chatroom(Model m) {
      return "chatroom";
   }

   /**
    * @MessageMapping("/hello")注解的方法将用来接收"/app/hello路径发送来的消息,<br/>
    * 在注解方法中对消息进行处理后,再将消息转发到 @SendTo 定义的路径上,而@SendTo路径是个前缀为"/topic"的路径,<br/>
    * 因此该消息将被交给消息代理 broker ,再由 broker 进行广播。
    */
   @MessageMapping("/hello")
   @SendTo("/topic/greetings")
   public SendMsgEO greeting(SendMsgEO message) throws Exception {
      return message;
   }
}


  • chatroom方法用来进入聊天页面,可以在其中设置聊天界面默认值;
  • greeting方法根据注解来接收与发送消息:
    • @MessageMapping("/hello")注解:将用来接收 "/app/hello" 路径发送来的消息,即greeting方法的message参数;
    • @SendTo("/topic/greetings")注解:greeting方法return的消息,会发转发到@SendTo定义的路径上;



package com.mall.websocket;
import lombok.Data;
@Data
public class SendMsgEO {
   private String username;
   private String content;
   public SendMsgEO(String username, String content) {
      super(); this.username = username;
      this.content = content;
   }
   public SendMsgEO() {
      super();
   }
}


  • spring会将前台js发送的消息,转化为SendMsgEO对象,所以要注意消息格式;



三、HTML页面JS代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>商城灌水集市</title>
</head>
<body>
   <table border="1">
      <tr>
         <td>
            <div id="msglist" style="width: 600px; height: 300px; border: 1px solid red; overflow-y: auto"></div>
         </td>
      </tr>
   </table>
   <table border="1" style="width: 600px;">
      <tr>
         <td>
            用户名:<input id="username" type="text" />
         </td>
         <td>
            <button id="connect" type="button">连接</button>
            <button id="disconnect" type="button">断开</button>
         </td>
         <td>
            <input id="content" type="text" placeholder="聊天内容........." />
            <button id="send" type="button">发送</button>
         </td>
      </tr>
   </table>
   <script type="text/javascript" th:src="@{/webjars/jquery/jquery.min.js}"></script>
   <script type="text/javascript" th:src="@{/webjars/sockjs-client/sockjs.min.js}"></script>
   <script type="text/javascript" th:src="@{/webjars/stomp-websocket/stomp.min.js}"></script>
   <script type="text/javascript" th:src="@{/bizjs/chatroom.js}"></script>
</body>
</html>
  • id为msglistdiv用来显示聊天记录;
  • 输入用户名后,点击连接按钮后,才能进行发送聊天消息;
let stompClient = null;
setConnected(false);
$("#username").val("jack_" + new Date());

function setConnected(connected) {
   $("#connect").prop("disabled", connected);
   $("#disconnect").prop("disabled", !connected);
   if (connected) {
      $("#content").show();
      $("#send").show();
   } else {
      $(" #content").hide();
      $("#send").hide();
   }
   $("#greetings").html("");
}

function connect() {
   if (!$("#username").val()) {
      alert("请输入用户名");
      return;
   }
   let socket = new SockJS('/chat');
   stompClient = Stomp.over(socket);
   stompClient.connect({}, connecting);
}

function connecting(frame) {
   setConnected(true);

   // subscribe方法用来订阅服务端发回来的消息
   stompClient.subscribe('/topic/greetings', receiveMsg);
}

/**
 * 接收服务器消息
 */
function receiveMsg(greeting) {
   showMsging(JSON.parse(greeting.body));
}

function disconnect() {
   if (stompClient !== null) {
      stompClient.disconnect();
      setConnected(false);
   }
}

function sendName() {
   stompClient.send("/app/hello", {}, JSON.stringify({
      "username" : $("#username").val(),
      "content" : $("#content").val()
   }));
}

function showMsging(message) {
   $("#msglist").append("<span>" + message.username + ":" + message.content + "</span><br/>");

   var div = document.getElementById('msglist');
   div.scrollTop = div.scrollHeight;
}

$(function() {
   $("#connect").click(function() {
      connect();
   });
   $("#disconnect").click(function() {
      disconnect();
   });
   $("#send").click(function() {
      sendName();
   });
});


  • connect 方法表示建立一个 WebSocket 连接,在建立 WebSocket 连接时,用户必须先输入用户名,后才能建立连接。
  • 其中使用 SockJS 建立连接,然后创建一个 STOMP 实例发起连接请求,在连接成功的回调方法中,首先调用 setConnected(true)方法进行页面的设置,然后调用 STOMP 中的 subscribe 方法订阅服务端发送回来的消息,并将服务端发送来的消息展示出来(使用 showGreeting 方法)。
  • 调用 STOMP 中的 disconnect 方法可以断开一个 WebSocket 连接