1. 概述
在本教程中,我們將創建一個簡單的 Web 應用程序,該應用程序使用 Spring Framework 4.0 中引入的 新 WebSocket 功能來實現消息傳遞。
WebSocket 是一種 雙向、全雙工、持久連接,它在 Web 瀏覽器和服務器之間建立。一旦 WebSocket 連接建立,該連接將保持開放,直到客户端或服務器決定關閉該連接。
典型的用例是當應用程序涉及多個用户相互通信,例如在聊天中。 我們將在此示例中構建一個簡單的聊天客户端。
2. Maven 依賴
由於該項目基於 Maven,因此首先需要在 pom.xml 中添加所需的依賴項:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>6.0.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>6.0.13</version>
</dependency>此外,由於我們將使用 JSON 構建消息體,還需要添加 Jackson 依賴項。
這使得 Spring 能夠將我們的 Java 對象轉換為 JSON 以及反之。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.17.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.2</version>
</dependency>查找上述庫的最新版本,請訪問 Maven Central。
3. 啓用 WebSocket 在 Spring
首先,我們啓用 WebSocket 功能。要做到這一點,我們需要將配置添加到我們的應用程序中,並使用 @EnableWebSocketMessageBroker 註解標記此類。
正如其名稱所示,它啓用 WebSocket 消息處理,並由消息代理支持。
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat");
registry.addEndpoint("/chat").withSockJS();
}
}
在這裏我們可以看到,configureMessageBroker 方法用於配置消息代理。
首先,我們啓用內存消息代理,以便將消息返回到以“/topic”為前綴的客户端目的地。
我們通過指定“/app”前綴來完成簡單的配置,從而過濾目標到帶有應用程序註解的方法(通過 @MessageMapping)的路由。
registerStompEndpoints 方法註冊了“/chat”端點,從而啓用 Spring 對 STOMP 協議的支持。
請注意,我們在這裏添加了一個無需 SockJS 也能工作的端點,以提高彈性。
當此端點以“/app”為前綴時,它映射到 ChatController.send() 方法來處理。
它還啓用 SockJS 備用選項,以便在 WebSockets 不可用時,可以使用替代消息選項。這在 WebSocket 在所有瀏覽器中尚未得到支持,並且可能受到限制的網絡代理影響時非常有用。
這些備用方案允許應用程序在運行時根據需要,優雅地降級到非 WebSocket 替代方案。
4. 創建消息模型
現在我們已經設置了項目並配置了 WebSocket 功能,我們需要創建一個消息進行發送。
該端點將接受包含發送者名稱和文本的 STOMP 消息,其消息主體是一個 JSON 對象。
消息可能如下所示:
{
"from": "John",
"text": "Hello!"
}
為了模擬傳輸文本的消息,我們可以創建一個簡單的Java對象,其中包含from和text屬性:
public class Message {
private String from;
private String text;
// getters and setters
}
默認情況下,Spring 將使用 Jackson 庫將我們的模型對象轉換為 JSON 格式,以及從 JSON 格式轉換為模型對象。
5. 創建消息處理控制器
正如我們所見,Spring 處理 STOMP 消息的方式是將控制器方法與配置的端點關聯起來。我們可以通過使用 @MessageMapping 標註來實現這一點。
端點和控制器之間的關聯使我們能夠在需要時處理消息:
@MessageMapping("/chat")
@SendTo("/topic/messages")
public OutputMessage send(Message message) throws Exception {
String time = new SimpleDateFormat("HH:mm").format(new Date());
return new OutputMessage(message.getFrom(), message.getText(), time);
}
在我們的示例中,我們將創建一個名為 OutputMessage 的模型對象,用於表示發送到配置的目標地的輸出消息。
我們用來自傳入消息的發送者和消息文本,並加上時間戳填充該對象。
在處理消息後,我們將它發送到定義的適當目標地,該目標地由 @SendTo 註解定義。所有訂閲了 “/topic/messages” 目標地的訂閲者都會收到該消息。
6. 創建瀏覽器客户端
在服務器端完成配置後,我們將使用 sockjs-client 庫,構建一個簡單的 HTML 頁面,以便與我們的消息系統進行交互。
首先,我們需要導入 sockjs 和 stomp JavaScript 客户端庫。
接下來,我們可以創建一個 connect() 函數來與我們的端點建立通信,創建一個 sendMessage() 函數來發送我們的 STOMP 消息,以及一個 disconnect() 函數來關閉通信。
<html>
<head>
<title>Chat WebSocket</title>
<script src="resources/js/sockjs-0.3.4.js"></script>
<script src="resources/js/stomp.js"></script>
<script type="text/javascript">
var stompClient = null;
function setConnected(connected) {
document.getElementById('connect').disabled = connected;
document.getElementById('disconnect').disabled = !connected;
document.getElementById('conversationDiv').style.visibility
= connected ? 'visible' : 'hidden';
document.getElementById('response').innerHTML = '';
}
function connect() {
var socket = new SockJS('/chat');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/messages', function(messageOutput) {
showMessageOutput(JSON.parse(messageOutput.body));
});
});
}
function disconnect() {
if(stompClient != null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
function sendMessage() {
var from = document.getElementById('from').value;
var text = document.getElementById('text').value;
stompClient.send("/app/chat", {},
JSON.stringify({'from':from, 'text':text}));
}
function showMessageOutput(messageOutput) {
var response = document.getElementById('response');
var p = document.createElement('p');
p.style.wordWrap = 'break-word';
p.appendChild(document.createTextNode(messageOutput.from + ": "
+ messageOutput.text + " (" + messageOutput.time + ")"));
response.appendChild(p);
}
</script>
</head>
<body onload="disconnect()">
<div>
<div>
<input type="text" id="from" placeholder="Choose a nickname"/>
</div>
<br />
<div>
<button id="connect" onclick="connect();">Connect</button>
<button id="disconnect" disabled="disabled" onclick="disconnect();">
Disconnect
</button>
</div>
<br />
<div id="conversationDiv">
<input type="text" id="text" placeholder="Write a message..."/>
<button id="sendMessage" onclick="sendMessage();">Send</button>
<p id="response"></p>
</div>
</div>
</body>
</html>7. 測試示例
為了測試我們的示例,我們可以打開幾個瀏覽器窗口並訪問聊天頁面:
http://localhost:8080完成這些步驟後,可以通過輸入暱稱並點擊“連接”按鈕加入聊天室。如果發送併發送一條消息,可以在所有已加入聊天室的瀏覽器會話中看到它。
請查看以下截圖:
8. 結論
本文介紹了 Spring 的 WebSocket 支持。我們已經瞭解了其服務器端配置,並使用 sockjs 和 stomp JavaScript 庫構建了一個簡單的客户端對應程序。