欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入淺出SpringBoot WebSocket構(gòu)建實(shí)時(shí)應(yīng)用全面指南

 更新時(shí)間:2025年07月30日 15:47:27   作者:超級小忍  
WebSocket 是一種在單個(gè) TCP 連接上進(jìn)行全雙工通信的協(xié)議,這篇文章主要為大家詳細(xì)介紹了SpringBoot如何集成WebSocket構(gòu)建實(shí)時(shí)應(yīng)用,感興趣的小伙伴可以了解下

前言

為什么需要 WebSocket

在傳統(tǒng)的 Web 應(yīng)用中,通信模式主要是 HTTP 請求-響應(yīng)??蛻舳耍ㄍǔJ菫g覽器)發(fā)起一個(gè)請求,服務(wù)器處理后返回一個(gè)響應(yīng),然后連接關(guān)閉。這種模式對于獲取網(wǎng)頁內(nèi)容、提交表單等操作非常有效。

然而,隨著 Web 應(yīng)用的復(fù)雜化,我們越來越多地需要實(shí)時(shí)、雙向、持續(xù)的通信能力。例如:

  • 在線聊天室: 用戶發(fā)送消息,所有在線用戶能立即看到。
  • 實(shí)時(shí)通知: 新郵件、好友請求、系統(tǒng)告警需要即時(shí)推送給用戶。
  • 股票行情/數(shù)據(jù)儀表盤: 價(jià)格、狀態(tài)需要秒級甚至毫秒級更新。
  • 在線游戲: 玩家狀態(tài)、游戲事件需要實(shí)時(shí)同步。
  • 協(xié)作編輯: 多人同時(shí)編輯文檔,彼此的修改需要實(shí)時(shí)可見。

如果使用傳統(tǒng)的 HTTP 輪詢(Polling)或長輪詢(Long Polling)來實(shí)現(xiàn)這些功能,會帶來巨大的服務(wù)器壓力、延遲高、效率低下。WebSocket 協(xié)議的出現(xiàn),正是為了解決這些問題。

WebSocket 是什么

WebSocket 是一種在單個(gè) TCP 連接上進(jìn)行全雙工(full-duplex)通信的協(xié)議。它允許服務(wù)器主動(dòng)向客戶端推送數(shù)據(jù),而無需客戶端先發(fā)起請求。一旦建立連接,客戶端和服務(wù)器就可以像打電話一樣,隨時(shí)向?qū)Ψ桨l(fā)送消息,實(shí)現(xiàn)真正的實(shí)時(shí)雙向通信。

Spring Boot 如何簡化 WebSocket 開發(fā)

Spring Boot 提供了強(qiáng)大的 spring-boot-starter-websocket 模塊,它基于 Spring Framework 的 WebSocket 支持,極大地簡化了在 Spring 應(yīng)用中集成 WebSocket 的過程。它不僅支持原始的 WebSocket API,還集成了 STOMP(Simple Text Oriented Messaging Protocol)協(xié)議,使得消息的發(fā)布/訂閱、點(diǎn)對點(diǎn)通信、用戶特定消息等復(fù)雜場景變得異常簡單。

第一部分:準(zhǔn)備工作

1.創(chuàng)建 Spring Boot 項(xiàng)目

使用 Spring Initializr (https://start.spring.io/) 創(chuàng)建一個(gè)新的項(xiàng)目。確保添加以下依賴:

pom.xml (Maven) 相關(guān)依賴示例:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
    <!-- 可選:用于模板渲染 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <!-- 可選:簡化代碼 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>
</dependencies>
  • Spring Web (spring-boot-starter-web)
  • Spring WebSocket (spring-boot-starter-websocket)
  • (可選) Thymeleaf (spring-boot-starter-thymeleaf) - 用于創(chuàng)建簡單的 HTML 前端頁面進(jìn)行演示。
  • (可選) Lombok - 簡化 Java 代碼(如 @Data, @AllArgsConstructor)。

2.項(xiàng)目結(jié)構(gòu)

一個(gè)典型的結(jié)構(gòu)可能如下:

src/
├── main/
│   ├── java/
│   │   └── com/example/websocketdemo/
│   │       ├── WebSocketConfig.java
│   │       ├── WebSocketController.java
│   │       ├── model/
│   │       │   └── Message.java
│   │       └── WebSocketDemoApplication.java
│   └── resources/
│       ├── static/
│       │   └── js/
│       │       └── app.js
│       └── templates/
│           └── index.html
└── test/
    └── ...

第二部分:配置 WebSocket (WebSocketConfig)

這是啟用和配置 WebSocket 功能的核心步驟。我們需要?jiǎng)?chuàng)建一個(gè)配置類。

package com.example.websocketdemo;

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;

/**
 * WebSocket 配置類
 * @EnableWebSocketMessageBroker 注解啟用 STOMP 消息代理功能。
 */
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    /**
     * 配置消息代理(Message Broker)
     * 消息代理負(fù)責(zé)處理來自客戶端的消息,并將消息廣播給訂閱了特定目的地的客戶端。
     *
     * @param config MessageBrokerRegistry
     */
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        // 1. 啟用一個(gè)簡單的內(nèi)存消息代理,用于處理以 "/topic" 或 "/queue" 開頭的消息。
        //    - "/topic" 通常用于**發(fā)布/訂閱**模式(一對多廣播)。
        //    - "/queue" 通常用于**點(diǎn)對點(diǎn)**模式(一對一,但多個(gè)訂閱者時(shí)會負(fù)載均衡)。
        config.enableSimpleBroker("/topic", "/queue");

        // 2. 定義應(yīng)用目的地前綴。
        //    所有以 "/app" 開頭的 STOMP 消息都會被路由到帶有 @MessageMapping 注解的控制器方法中。
        //    例如:客戶端發(fā)送到 "/app/hello" 的消息會被 @MessageMapping("/hello") 的方法處理。
        config.setApplicationDestinationPrefixes("/app");

        // (可選) 設(shè)置用戶目的地前綴 (用于用戶特定消息)
        // config.setUserDestinationPrefix("/user");
    }

    /**
     * 注冊 STOMP 協(xié)議的 WebSocket 端點(diǎn)。
     * 客戶端通過這些端點(diǎn)與服務(wù)器建立 WebSocket 連接。
     *
     * @param registry StompEndpointRegistry
     */
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        // 1. 注冊一個(gè)名為 "/ws" 的端點(diǎn)。
        //    客戶端將連接到 "ws://<server>:<port>/ws" (HTTP) 或 "wss://<server>:<port>/ws" (HTTPS)。
        registry.addEndpoint("/ws")

                // 2. 啟用 SockJS 作為后備機(jī)制。
                //    SockJS 是一個(gè) JavaScript 庫,它在瀏覽器不支持原生 WebSocket 時(shí),
                //    會嘗試使用其他技術(shù)(如輪詢)來模擬 WebSocket 行為,提高兼容性。
                //    客戶端連接時(shí),如果使用 SockJS,URL 會是 "/ws/sockjs/info" 等。
                .withSockJS();

        // (可選) 可以注冊多個(gè)端點(diǎn)
        // registry.addEndpoint("/another-endpoint").withSockJS();
    }
}

關(guān)鍵點(diǎn)解析:

@EnableWebSocketMessageBroker: 這個(gè)注解是開啟 Spring WebSocket 支持的關(guān)鍵,它啟用了 STOMP 消息代理。

configureMessageBroker:

  • enableSimpleBroker(...): 啟用一個(gè)簡單的內(nèi)存消息代理。對于生產(chǎn)環(huán)境,你可能需要集成更強(qiáng)大的消息代理,如 RabbitMQRedis(通過 @EnableStompBrokerRelay 配置),以實(shí)現(xiàn)集群部署和消息持久化。
  • setApplicationDestinationPrefixes(...): 定義了應(yīng)用處理消息的前綴。/app 是約定俗成的前綴。

registerStompEndpoints:

  • addEndpoint("/ws"): 定義了 WebSocket 連接的實(shí)際路徑。
  • .withSockJS(): 強(qiáng)烈建議啟用,以確保在老舊瀏覽器或網(wǎng)絡(luò)環(huán)境下的兼容性。

第三部分:定義消息模型 (Message.java)

創(chuàng)建一個(gè)簡單的 POJO 類來表示我們要發(fā)送和接收的消息。

package com.example.websocketdemo.model;

import lombok.Data;
import lombok.AllArgsConstructor;

/**
 * 消息實(shí)體類
 */
@Data
@AllArgsConstructor
public class Message {
    private String content; // 消息內(nèi)容
    private String sender;  // 發(fā)送者
    private long timestamp; // 時(shí)間戳

    // 無參構(gòu)造函數(shù)(JSON 反序列化需要)
    public Message() {}

    // (可選) 可以添加更多字段,如消息類型、接收者等
}

第四部分:創(chuàng)建 WebSocket 控制器 (WebSocketController.java)

這個(gè)控制器負(fù)責(zé)處理來自客戶端的消息(通過 @MessageMapping)以及向客戶端發(fā)送消息(通過 SimpMessagingTemplate)。

package com.example.websocketdemo;

import com.example.websocketdemo.model.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.util.HtmlUtils;

import java.time.Instant;

/**
 * WebSocket 消息處理控制器
 */
@Controller // 使用 @Controller 而不是 @RestController,因?yàn)橥ǔ2恢苯臃祷?HTTP 響應(yīng)
public class WebSocketController {

    // SimpMessagingTemplate 用于從服務(wù)器端任意位置向客戶端發(fā)送消息
    @Autowired
    private SimpMessagingTemplate messagingTemplate;

    /**
     * 處理客戶端發(fā)送到 "/app/hello" 的消息。
     * 此方法將處理消息,并將處理后的結(jié)果廣播給所有訂閱了 "/topic/greetings" 的客戶端。
     *
     * @param message 客戶端發(fā)送的原始消息 (Message 對象)
     * @return 處理后的消息 (Message 對象) - 這個(gè)返回值會被 @SendTo 指定的目的地接收
     * @throws Exception
     */
    @MessageMapping("/hello") // 監(jiān)聽目的地 "/app/hello"
    @SendTo("/topic/greetings") // 將方法返回值發(fā)送到 "/topic/greetings"
    public Message greeting(@Payload Message message) throws Exception {
        // 模擬一些處理延遲
        Thread.sleep(1000);

        // 返回一個(gè)處理后的消息,包含原內(nèi)容、發(fā)送者和當(dāng)前時(shí)間戳
        return new Message(
                "Hello, " + HtmlUtils.htmlEscape(message.getSender()) + "!",
                "Server",
                Instant.now().toEpochMilli()
        );
    }

    /**
     * 處理客戶端發(fā)送到 "/app/chat" 的消息。
     * 這個(gè)方法展示了如何使用 SimpMessagingTemplate 進(jìn)行更靈活的消息發(fā)送。
     * 它不會返回值給 @SendTo,而是直接使用 messagingTemplate 發(fā)送消息。
     *
     * @param message 客戶端發(fā)送的聊天消息
     */
    @MessageMapping("/chat")
    public void handleChatMessage(@Payload Message message) {
        // 可以在這里進(jìn)行消息驗(yàn)證、存儲到數(shù)據(jù)庫等操作
        // ...

        // 使用 SimpMessagingTemplate 將消息廣播給所有訂閱了 "/topic/chat" 的客戶端
        messagingTemplate.convertAndSend("/topic/chat", message);

        // (示例) 向特定用戶發(fā)送消息 (需要配置用戶目的地前綴)
        // messagingTemplate.convertAndSendToUser("username", "/queue/private", privateMessage);
    }

    /**
     * (可選) 示例:從服務(wù)器內(nèi)部其他地方(如定時(shí)任務(wù)、服務(wù))觸發(fā)消息發(fā)送
     */
    // @Scheduled(fixedRate = 5000)
    // public void sendServerTime() {
    //     Message timeMessage = new Message("Server Time: " + Instant.now(), "System", Instant.now().toEpochMilli());
    //     messagingTemplate.convertAndSend("/topic/greetings", timeMessage);
    // }
}

關(guān)鍵點(diǎn)解析:

  • @Controller: 標(biāo)記為控制器。

@MessageMapping("/hello"): 將方法映射到 STOMP 消息的目的地 /app/hello??蛻舳税l(fā)送到 /app/hello 的消息會觸發(fā)此方法。

@Payload: 明確指定參數(shù)是從消息體(Payload)中提取并反序列化為 Message 對象的。

@SendTo("/topic/greetings"): 指定該方法的返回值應(yīng)該發(fā)送到 /topic/greetings 這個(gè)目的地。所有訂閱了此目的地的客戶端都會收到此消息。

SimpMessagingTemplate: 這是一個(gè)強(qiáng)大的工具,允許你在代碼的任何地方(而不僅限于 @MessageMapping 方法)發(fā)送消息。

  • convertAndSend(destination, payload) 方法會將 payload 對象序列化(通常是 JSON)并發(fā)送到指定的 destination。
  • convertAndSendToUser(user, destination, payload) 用于向特定用戶發(fā)送消息(需要配置用戶目的地前綴和用戶識別機(jī)制)。

第五部分:創(chuàng)建前端頁面 (index.html)

使用 Thymeleaf 創(chuàng)建一個(gè)簡單的 HTML 頁面來測試我們的 WebSocket 功能。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
  <head>
    <meta charset="UTF-8" />
    <title>Spring Boot WebSocket Demo</title>
    <!-- 引入 SockJS 客戶端庫 (如果配置了 withSockJS) -->
    <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
    <!-- 引入 STOMP 客戶端庫 -->
    <script src="https://cdn.jsdelivr.net/npm/@stomp/stompjs@6.1.0/bundles/stomp.umd.min.js"></script>
    <!-- (可選) Bootstrap 用于美化 -->
    <link
       rel="external nofollow" 
      rel="stylesheet"
    />
  </head>
  <body>
    <div class="container mt-5">
      <h1>WebSocket Chat & Greeting Demo</h1>

      <div class="row">
        <div class="col-md-6">
          <h3>Send Greeting</h3>
          <form id="greetingForm">
            <div class="mb-3">
              <label for="greetingSender" class="form-label">Your Name:</label>
              <input
                type="text"
                class="form-control"
                id="greetingSender"
                placeholder="Enter your name"
                required
              />
            </div>
            <button type="submit" class="btn btn-primary">Send Greeting</button>
          </form>
        </div>

        <div class="col-md-6">
          <h3>Chat Room</h3>
          <form id="chatForm">
            <div class="mb-3">
              <label for="chatSender" class="form-label">Nickname:</label>
              <input
                type="text"
                class="form-control"
                id="chatSender"
                placeholder="Enter nickname"
                required
              />
            </div>
            <div class="mb-3">
              <label for="chatMessage" class="form-label">Message:</label>
              <textarea
                class="form-control"
                id="chatMessage"
                rows="3"
                placeholder="Type your message..."
                required
              ></textarea>
            </div>
            <button type="submit" class="btn btn-success">Send Message</button>
          </form>
        </div>
      </div>

      <div class="row mt-4">
        <div class="col-md-6">
          <h3>Greetings Received</h3>
          <ul id="greetingList" class="list-group"></ul>
        </div>
        <div class="col-md-6">
          <h3>Chat Messages</h3>
          <ul id="chatList" class="list-group"></ul>
        </div>
      </div>
    </div>

    <!-- 引入自定義 JavaScript -->
    <script th:src="@{/js/app.js}"></script>
  </body>
</html>

第六部分:編寫前端 JavaScript (app.js)

這是前端與 WebSocket 交互的核心邏輯。

// 定義全局變量
let stompClient = null
let connected = false

// 頁面加載完成后執(zhí)行
document.addEventListener("DOMContentLoaded", function () {
  connect()
})

// 連接到 WebSocket 服務(wù)器
function connect() {
  // 1. 創(chuàng)建 SockJS 實(shí)例,連接到后端配置的端點(diǎn) "/ws"
  //    如果后端沒有配置 withSockJS,則使用 new WebSocket("ws://localhost:8080/ws");
  const socket = new SockJS("/ws") // 注意:路徑是相對于當(dāng)前頁面的,這里假設(shè)在根路徑

  // 2. 使用 SockJS 實(shí)例創(chuàng)建 STOMP 客戶端
  stompClient = Stomp.over(socket)

  // 3. 連接到 STOMP 代理
  stompClient.connect(
    {},
    function (frame) {
      console.log("Connected: " + frame)
      connected = true
      // 更新 UI 狀態(tài) (可選)
      // document.getElementById('connectionStatus').innerHTML = 'Connected';

      // 4. 訂閱目的地 "/topic/greetings"
      //    當(dāng)服務(wù)器向 "/topic/greetings" 發(fā)送消息時(shí),onGreetingReceived 函數(shù)會被調(diào)用
      stompClient.subscribe("/topic/greetings", onGreetingReceived)

      // 5. 訂閱目的地 "/topic/chat"
      stompClient.subscribe("/topic/chat", onChatMessageReceived)
    },
    function (error) {
      console.error("Connection error: " + error)
      connected = false
      // 重連邏輯 (可選)
      // setTimeout(function() { connect(); }, 5000);
    }
  )
}

// 處理從 "/topic/greetings" 接收到的消息
function onGreetingReceived(payload) {
  const message = JSON.parse(payload.body)
  const greetingList = document.getElementById("greetingList")
  const item = document.createElement("li")
  item.textContent = `[${new Date(message.timestamp).toLocaleTimeString()}] ${
    message.sender
  }: ${message.content}`
  item.className = "list-group-item list-group-item-info"
  greetingList.appendChild(item)
  // 自動(dòng)滾動(dòng)到底部
  greetingList.scrollTop = greetingList.scrollHeight
}

// 處理從 "/topic/chat" 接收到的消息
function onChatMessageReceived(payload) {
  const message = JSON.parse(payload.body)
  const chatList = document.getElementById("chatList")
  const item = document.createElement("li")
  item.textContent = `[${new Date(message.timestamp).toLocaleTimeString()}] ${
    message.sender
  }: ${message.content}`
  item.className = "list-group-item"
  chatList.appendChild(item)
  chatList.scrollTop = chatList.scrollHeight
}

// 處理 "Send Greeting" 表單提交
document
  .getElementById("greetingForm")
  .addEventListener("submit", function (event) {
    event.preventDefault() // 阻止表單默認(rèn)提交行為
    const senderInput = document.getElementById("greetingSender")
    const sender = senderInput.value.trim()
    if (sender && connected) {
      // 發(fā)送消息到目的地 "/app/hello"
      // 消息體是一個(gè) JSON 字符串
      stompClient.send(
        "/app/hello",
        {},
        JSON.stringify({ sender: sender, content: "Greeting Request" })
      )
      senderInput.value = "" // 清空輸入框
    } else if (!connected) {
      alert("WebSocket not connected!")
    }
  })

// 處理 "Send Message" 表單提交
document
  .getElementById("chatForm")
  .addEventListener("submit", function (event) {
    event.preventDefault()
    const senderInput = document.getElementById("chatSender")
    const messageInput = document.getElementById("chatMessage")
    const sender = senderInput.value.trim()
    const content = messageInput.value.trim()
    if (sender && content && connected) {
      // 發(fā)送消息到目的地 "/app/chat"
      const chatMessage = {
        sender: sender,
        content: content,
        timestamp: new Date().getTime(), // 客戶端時(shí)間戳,服務(wù)器會用自己的
      }
      stompClient.send("/app/chat", {}, JSON.stringify(chatMessage))
      // 清空輸入框
      messageInput.value = ""
      // (可選) 立即將消息顯示在本地聊天列表(回顯),服務(wù)器廣播后會再次收到
      // onChatMessageReceived({body: JSON.stringify(chatMessage)});
    } else if (!connected) {
      alert("WebSocket not connected!")
    }
  })

// (可選) 斷開連接函數(shù)
function disconnect() {
  if (stompClient) {
    stompClient.disconnect()
    connected = false
    console.log("Disconnected")
    // 更新 UI 狀態(tài)
    // document.getElementById('connectionStatus').innerHTML = 'Disconnected';
  }
}

// 頁面卸載時(shí)斷開連接
window.addEventListener("beforeunload", function () {
  disconnect()
})

關(guān)鍵點(diǎn)解析:

  • SockJS('/ws'): 創(chuàng)建 SockJS 連接,路徑必須與后端 WebSocketConfigaddEndpoint("/ws") 一致。
  • Stomp.over(socket): 使用 SockJS 連接創(chuàng)建 STOMP 客戶端。
  • stompClient.connect(headers, connectCallback, errorCallback): 連接到 STOMP 代理。headers 通常為空對象 {}
  • stompClient.subscribe(destination, callback): 訂閱一個(gè)目的地。callback 函數(shù)接收一個(gè) payload 參數(shù),其 body 屬性是服務(wù)器發(fā)送的原始消息字符串(通常是 JSON)。
  • stompClient.send(destination, headers, body): 向指定目的地發(fā)送消息。body 是消息內(nèi)容(字符串)。
  • JSON.parse(payload.body): 將接收到的 JSON 字符串解析成 JavaScript 對象。
  • JSON.stringify(object): 將 JavaScript 對象序列化成 JSON 字符串發(fā)送。

第七部分:運(yùn)行與測試

啟動(dòng)應(yīng)用: 運(yùn)行 WebSocketDemoApplication.javamain 方法。

訪問頁面: 打開瀏覽器,訪問 http://localhost:8080 (或你配置的端口和路徑)。

觀察控制臺: 打開瀏覽器的開發(fā)者工具(F12),查看 Network 和 Console 標(biāo)簽頁。你應(yīng)該能看到 SockJS 或 WebSocket 連接建立成功 (CONNECTED 幀)。

測試功能:

  • 在 “Send Greeting” 區(qū)域輸入名字并點(diǎn)擊 “Send Greeting”。稍等 1 秒,你會在 “Greetings Received” 列表中看到服務(wù)器返回的 “Hello, [你的名字]!” 消息。
  • 在 “Chat Room” 區(qū)域輸入昵稱和消息,點(diǎn)擊 “Send Message”。消息會立即出現(xiàn)在 “Chat Messages” 列表中(因?yàn)榉?wù)器廣播回所有客戶端,包括發(fā)送者)。
  • 打開多個(gè)瀏覽器標(biāo)簽頁或窗口訪問同一個(gè)頁面。在一個(gè)窗口發(fā)送消息,其他所有窗口都會實(shí)時(shí)收到更新!這就是 WebSocket 的魔力。

第八部分:高級主題與最佳實(shí)踐

1.用戶認(rèn)證與授權(quán) (Security):

  • 通常需要將 WebSocket 連接與用戶的登錄會話關(guān)聯(lián)??梢栽?WebSocketConfigregisterStompEndpoints 中添加攔截器,或者在 HttpSessionHandshakeInterceptor 中將用戶信息存入 WebSocketSession 的屬性。
  • 使用 Spring Security 保護(hù) /ws 端點(diǎn),確保只有認(rèn)證用戶才能連接。
  • @MessageMapping 方法上使用 @PreAuthorize 進(jìn)行細(xì)粒度權(quán)限控制。
  • 使用 messagingTemplate.convertAndSendToUser(username, destination, payload) 向特定用戶發(fā)送私有消息。需要配置 setUserDestinationPrefix("/user")。

2.消息代理 (Message Broker):

Simple Broker: 適用于單機(jī)部署的簡單應(yīng)用。在集群環(huán)境下,不同實(shí)例間的客戶端無法互相通信。

STOMP Broker Relay (推薦生產(chǎn)環(huán)境): 配置 Spring Boot 應(yīng)用連接到外部的、功能更強(qiáng)大的 STOMP 消息代理(如 RabbitMQ, ActiveMQ, Redis)。

// 在 WebSocketConfig 中
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
    // 配置應(yīng)用目的地前綴
    config.setApplicationDestinationPrefixes("/app");
    // 配置用戶目的地前綴
    config.setUserDestinationPrefix("/user");

    // 啟用 STOMP 代理中繼,連接到外部的 Broker
    config.enableStompBrokerRelay("/topic", "/queue")
          .setRelayHost("localhost") // 外部 Broker 的主機(jī)
          .setRelayPort(61613)      // STOMP 端口 (RabbitMQ 默認(rèn) 61613)
          .setClientLogin("guest")  // Broker 用戶名
          .setClientPasscode("guest"); // Broker 密碼
}

優(yōu)勢: 支持集群、消息持久化、更復(fù)雜的消息路由、高可用性。

3.異常處理:

  • 可以使用 @ControllerAdvice@MessageExceptionHandler 注解來處理 @MessageMapping 方法中拋出的異常,并向客戶端發(fā)送錯(cuò)誤消息。
  • 處理連接斷開 (WebSocketSession 關(guān)閉) 的邏輯。

4.性能與監(jiān)控:

  • 監(jiān)控連接數(shù)、消息吞吐量。
  • 考慮消息大小和頻率,避免網(wǎng)絡(luò)擁塞。
  • 對于高并發(fā)場景,優(yōu)化線程池配置。

5.前端庫選擇:

  • @stomp/stompjs 是目前最流行和維護(hù)良好的 STOMP 客戶端庫。
  • sockjs-client 是 SockJS 的官方庫。

第九部分:總結(jié)

通過本文的詳細(xì)步驟,我們成功地在 Spring Boot 應(yīng)用中集成并實(shí)現(xiàn)了 WebSocket 功能。我們學(xué)習(xí)了:

  • 核心概念: WebSocket 協(xié)議、STOMP、消息代理、發(fā)布/訂閱模式。
  • 配置: 使用 @EnableWebSocketMessageBrokerWebSocketMessageBrokerConfigurer 進(jìn)行配置。
  • 后端開發(fā): 使用 @MessageMapping, @SendTo, SimpMessagingTemplate 處理消息和發(fā)送消息。
  • 前端開發(fā): 使用 sockjs-client@stomp/stompjs 庫建立連接、訂閱、發(fā)送消息。
  • 高級主題: 安全、消息代理、異常處理。

Spring Boot 的 WebSocket 支持使得構(gòu)建實(shí)時(shí) Web 應(yīng)用變得相對簡單和高效。掌握這些知識,你就可以為你的應(yīng)用添加強(qiáng)大的實(shí)時(shí)交互能力了。

下一步:

  • 嘗試集成 Spring Security 進(jìn)行用戶認(rèn)證。
  • 將簡單消息代理替換為 RabbitMQ 或 Redis。
  • 實(shí)現(xiàn)更復(fù)雜的聊天功能,如群組、在線狀態(tài)、消息歷史記錄。
  • 探索 WebSocket 在游戲、協(xié)作工具等領(lǐng)域的應(yīng)用。

以上就是深入淺出SpringBoot WebSocket構(gòu)建實(shí)時(shí)應(yīng)用全面指南的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot WebSocket構(gòu)建實(shí)時(shí)應(yīng)用的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java實(shí)現(xiàn)的DES加密解密工具類實(shí)例

    Java實(shí)現(xiàn)的DES加密解密工具類實(shí)例

    這篇文章主要介紹了Java實(shí)現(xiàn)的DES加密解密工具類,結(jié)合具體實(shí)例形式分析了Java實(shí)現(xiàn)的DES加密解密工具類定義與使用方法,需要的朋友可以參考下
    2017-09-09
  • Java并發(fā)教程之Callable和Future接口詳解

    Java并發(fā)教程之Callable和Future接口詳解

    Java從發(fā)布的第一個(gè)版本開始就可以很方便地編寫多線程的應(yīng)用程序,并在設(shè)計(jì)中引入異步處理,這篇文章主要給大家介紹了關(guān)于Java并發(fā)教程之Callable和Future接口的相關(guān)資料,需要的朋友可以參考下
    2021-07-07
  • Java操作MongoDB數(shù)據(jù)庫的示例代碼

    Java操作MongoDB數(shù)據(jù)庫的示例代碼

    這篇文章主要介紹了Java操作MongoDB的示例代碼,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下
    2021-04-04
  • java靜態(tài)工具類注入service出現(xiàn)NullPointerException異常處理

    java靜態(tài)工具類注入service出現(xiàn)NullPointerException異常處理

    如果我們要在我們自己封裝的Utils工具類中或者非controller普通類中使用@Autowired注解注入Service或者M(jìn)apper接口,直接注入是報(bào)錯(cuò)的,因Utils用了靜態(tài)方法,我們無法直接用非靜態(tài)接口的,遇到這問題,我們要想法解決,下面小編就簡單介紹解決辦法,需要的朋友可參考下
    2021-09-09
  • Java調(diào)用python的方法(jython)

    Java調(diào)用python的方法(jython)

    這篇文章主要介紹了Java調(diào)用python的方法(jython),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-06-06
  • SpringBoot中配置Redis連接池的完整指南

    SpringBoot中配置Redis連接池的完整指南

    這篇文章主要為大家詳細(xì)介紹了SpringBoot中配置Redis連接池的完整指南,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-04-04
  • ?基于Java解決華為機(jī)試之字符串合并處理實(shí)操

    ?基于Java解決華為機(jī)試之字符串合并處理實(shí)操

    這篇文章主要介紹了基于Java解決華為機(jī)試之字符串合并處理,文章以實(shí)操展開主題內(nèi)容,具有一的參考價(jià)值,需要的小伙伴可以參考一下,希望對工作中的你有所幫助
    2022-02-02
  • 基于HttpServletResponse 相關(guān)常用方法的應(yīng)用

    基于HttpServletResponse 相關(guān)常用方法的應(yīng)用

    本篇文章小編為大家介紹,基于HttpServletResponse 相關(guān)常用方法的應(yīng)用,需要的朋友參考下
    2013-04-04
  • Java通過動(dòng)態(tài)代理實(shí)現(xiàn)一個(gè)簡單的攔截器操作

    Java通過動(dòng)態(tài)代理實(shí)現(xiàn)一個(gè)簡單的攔截器操作

    這篇文章主要介紹了Java通過動(dòng)態(tài)代理實(shí)現(xiàn)一個(gè)簡單的攔截器操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Java聊天室之實(shí)現(xiàn)獲取Socket功能

    Java聊天室之實(shí)現(xiàn)獲取Socket功能

    這篇文章主要為大家詳細(xì)介紹了Java簡易聊天室之實(shí)現(xiàn)獲取遠(yuǎn)程服務(wù)器和客戶機(jī)的IP地址和端口號功能,文中的示例代碼講解詳細(xì),需要的可以了解一下
    2022-10-10

最新評論