本指南将引导您完成创建“Hello, world”应用程序的过程,该应用程序在浏览器和服务器之间来回发送消息。WebSocket 是 TCP 之上的一层薄而轻的层。这使得它适合使用“子协议”来嵌入消息。在本指南中,我们使用踩使用 Spring 发送消息以创建交互式 Web 应用程序。STOMP是在较低级别的WebSocket上运行的子协议。
您将构建什么
您将构建一个接受带有用户名的消息的服务器。作为响应,服务器会将问候语推送到客户端订阅的队列中。
你需要什么
- 约15分钟
- 最喜欢的文本编辑器或 IDE
- JDK 1.8或以后
- 格拉德尔 4+或梅文 3.2+
- 您也可以将代码直接导入到 IDE 中:
如何完成本指南
像大多数春天一样入门指南,您可以从头开始并完成每个步骤,也可以绕过您已经熟悉的基本设置步骤。无论哪种方式,您最终都会得到工作代码。
要从头开始,请继续从 Spring 初始化开始.
要跳过基础知识,请执行以下操作:
- 下载并解压缩本指南的源存储库,或使用吉特:
git clone https://github.com/spring-guides/gs-messaging-stomp-websocket.git
- 光盘成
gs-messaging-stomp-websocket/initial
- 跳转到创建资源表示类.
完成后,您可以根据 中的代码检查结果。gs-messaging-stomp-websocket/complete
从 Spring 初始化开始
你可以使用这个预初始化项目,然后单击生成以下载 ZIP 文件。此项目配置为适合本教程中的示例。
手动初始化项目:
- 导航到https://start.spring.io.此服务拉入应用程序所需的所有依赖项,并为您完成大部分设置。
- 选择 Gradle 或 Maven 以及您要使用的语言。本指南假定您选择了 Java。
- 单击“依赖项”,然后选择“Websocket”。
- 单击生成。
- 下载生成的 ZIP 文件,该文件是配置了您选择的 Web 应用程序的存档。
If your IDE has the Spring Initializr integration, you can complete this process from your IDE. |
You can also fork the project from Github and open it in your IDE or other editor. |
Adding Dependencies
The Spring Initializr does not provide everything you need in this case. For Maven, you need to add the following dependencies:
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator-core</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>sockjs-client</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>stomp-websocket</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.1.1-1</version>
</dependency>
以下清单显示了完成的文件:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>messaging-stomp-websocket-complete</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>messaging-stomp-websocket-complete</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator-core</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>sockjs-client</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>stomp-websocket</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.1.1-1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
如果您使用 Gradle,则需要添加以下依赖项:
implementation 'org.webjars:webjars-locator-core'
implementation 'org.webjars:sockjs-client:1.0.2'
implementation 'org.webjars:stomp-websocket:2.3.3'
implementation 'org.webjars:bootstrap:3.3.7'
implementation 'org.webjars:jquery:3.1.1-1'
以下清单显示了完成的文件:build.gradle
plugins {
id 'org.springframework.boot' version '3.0.0'
id 'io.spring.dependency-management' version '1.1.0'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-websocket'
implementation 'org.webjars:webjars-locator-core'
implementation 'org.webjars:sockjs-client:1.0.2'
implementation 'org.webjars:stomp-websocket:2.3.3'
implementation 'org.webjars:bootstrap:3.3.7'
implementation 'org.webjars:jquery:3.1.1-1'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}
创建资源表示类
设置项目和生成系统后,可以创建 STOMP 消息服务。
从考虑服务交互开始该过程。
该服务将接受正文为 JSON 对象的 STOMP 消息中包含名称的消息。如果名称为 ,则消息可能类似于以下内容:Fred
要对带有该名称的消息进行建模,您可以创建一个带有属性和相应方法的普通旧 Java 对象,如以下清单 (from ) 所示:name
getName()
src/main/java/com/example/messagingstompwebsocket/HelloMessage.java
package com.example.messagingstompwebsocket;
public class HelloMessage {
private String name;
public HelloMessage() {
}
public HelloMessage(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
收到消息并提取名称后,服务将通过创建问候语并在客户端订阅的单独队列上发布该问候语来处理它。问候语也将是一个 JSON 对象,如以下列表所示:
{
"content": "Hello, Fred!"
}
要对问候语表示进行建模,请添加另一个具有属性和相应方法的普通旧 Java 对象,如以下清单 (from ) 所示:content
getContent()
src/main/java/com/example/messagingstompwebsocket/Greeting.java
package com.example.messagingstompwebsocket;
public class Greeting {
private String content;
public Greeting() {
}
public Greeting(String content) {
this.content = content;
}
public String getContent() {
return content;
}
}
春天将使用杰克逊·库,用于自动将类型的实例封送到 JSON 中。Greeting
接下来,您将创建一个控制器来接收 hello 消息并发送问候消息。
创建消息处理控制器
在 Spring 处理 STOMP 消息传递的方法中,STOMP 消息可以路由到@Controller类。例如,将 (from ) 映射为处理到目标的消息,如以下清单所示:GreetingController
src/main/java/com/example/messagingstompwebsocket/GreetingController.java
/hello
package com.example.messagingstompwebsocket;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import org.springframework.web.util.HtmlUtils;
@Controller
public class GreetingController {
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {
Thread.sleep(1000); // simulated delay
return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
}
}
这个控制器简洁明了,但很多事情都在发生。我们一步一步地分解它。
这@MessageMapping注释可确保在将消息发送到目标时调用该方法。/hello
greeting()
消息的有效负载绑定到一个对象,该对象被传递到 .HelloMessage
greeting()
在内部,该方法的实现通过使线程休眠一秒钟来模拟处理延迟。这是为了演示在客户端发送消息后,服务器可能需要异步处理消息的时间。客户端可以继续执行它需要做的任何工作,而无需等待响应。
在一秒钟的延迟之后,该方法创建一个对象并返回它。返回值将广播给 的所有订阅者,如greeting()
Greeting
/topic/greetings
@SendTo注解。请注意,输入消息中的名称经过净化,因为在这种情况下,它将被回显并在客户端的浏览器 DOM 中重新呈现。
为 STOMP 消息传递配置 Spring
现在,服务的基本组件已经创建,您可以配置 Spring 以启用 WebSocket 和 STOMP 消息传递。
创建一个名为类似于以下清单的 Java 类(来自):WebSocketConfig
src/main/java/com/example/messagingstompwebsocket/WebSocketConfig.java
package com.example.messagingstompwebsocket;
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
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gs-guide-websocket").withSockJS();
}
}
WebSocketConfig
注释 以指示它是一个 Spring 配置类。它还带有注释@Configuration
@EnableWebSocketMessageBroker.顾名思义,启用由消息代理支持的 WebSocket 消息处理。@EnableWebSocketMessageBroker
该方法实现默认方法以配置消息代理。它首先调用,使基于内存的简单消息代理能够将问候消息传送回前缀为 .它还指定绑定到用 注释的方法的消息的前缀。此前缀将用于定义所有消息映射。例如,是方法映射到处理的终结点。configureMessageBroker()
WebSocketMessageBrokerConfigurer
enableSimpleBroker()
/topic
/app
@MessageMapping
/app/hello
GreetingController.greeting()
该方法注册终结点,启用 SockJS 回退选项,以便在 WebSocket 不可用时使用备用传输。SockJS 客户端将尝试连接并使用最佳可用传输(websocket、xhr-streaming、xhr-polling 等)。registerStompEndpoints()
/gs-guide-websocket
/gs-guide-websocket
创建浏览器客户端
准备好服务器端部分后,您可以将注意力转向 JavaScript 客户端,该客户端将向服务器端发送消息和从服务器端接收消息。
创建类似于以下清单的文件(来自):index.html
src/main/resources/static/index.html
<!DOCTYPE html>
<html>
<head>
<title>Hello WebSocket</title>
<link href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="/main.css" rel="stylesheet">
<script src="/webjars/jquery/jquery.min.js"></script>
<script src="/webjars/sockjs-client/sockjs.min.js"></script>
<script src="/webjars/stomp-websocket/stomp.min.js"></script>
<script src="/app.js"></script>
</head>
<body>
<noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being
enabled. Please enable
Javascript and reload this page!</h2></noscript>
<div class="container">
<div class="row">
<div class="col-md-6">
<form class="form-inline">
<div class="form-group">
<label for="connect">WebSocket connection:</label>
<button class="btn btn-default" type="submit">Connect</button>
<button class="btn btn-default" type="submit" disabled="disabled">Disconnect
</button>
</div>
</form>
</div>
<div class="col-md-6">
<form class="form-inline">
<div class="form-group">
<label for="name">What is your name?</label>
<input type="text" class="form-control" placeholder="Your name here...">
</div>
<button class="btn btn-default" type="submit">Send</button>
</form>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table class="table table-striped">
<thead>
<tr>
<th>Greetings</th>
</tr>
</thead>
<tbody >
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>
此HTML文件导入了将用于通过websocket上的STOMP与我们的服务器进行通信的javascript库。我们还导入 ,其中包含客户端应用程序的逻辑。以下清单(来自 )显示了该文件:SockJS
STOMP
app.js
src/main/resources/static/app.js
var stompClient = null;
function setConnected(connected) {
$("#connect").prop("disabled", connected);
$("#disconnect").prop("disabled", !connected);
if (connected) {
$("#conversation").show();
}
else {
$("#conversation").hide();
}
$("#greetings").html("");
}
function connect() {
var socket = new SockJS('/gs-guide-websocket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/greetings', function (greeting) {
showGreeting(JSON.parse(greeting.body).content);
});
});
}
function disconnect() {
if (stompClient !== null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
function sendName() {
stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()}));
}
function showGreeting(message) {
$("#greetings").append("<tr><td>" + message + "</td></tr>");
}
$(function () {
$("form").on('submit', function (e) {
e.preventDefault();
});
$( "#connect" ).click(function() { connect(); });
$( "#disconnect" ).click(function() { disconnect(); });
$( "#send" ).click(function() { sendName(); });
});
要理解的这个JavaScript文件的主要部分是和函数。connect()
sendName()
该函数使用connect()
袜子和跺脚.js以打开与 的连接,这是我们的 SockJS 服务器等待连接的地方。连接成功后,客户端订阅目标,服务器将在其中发布问候消息。当在该目标上收到问候语时,它会将段落元素附加到 DOM 以显示问候消息。/gs-guide-websocket
/topic/greetings
该函数检索用户输入的名称,并使用 STOMP 客户端将其发送到目标(将接收它的位置)。sendName()
/app/hello
GreetingController.greeting()
如果您愿意,可以省略,也可以创建一个空的,以便可以解决。main.css
<link>
使应用程序可执行
Spring Boot 会为你创建一个应用程序类。在这种情况下,它不需要进一步修改。您可以使用它来运行此应用程序。以下清单(来自 )显示了应用程序类:src/main/java/com/example/messagingstompwebsocket/MessagingStompWebsocketApplication.java
package com.example.messagingstompwebsocket;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MessagingStompWebsocketApplication {
public static void main(String[] args) {
SpringApplication.run(MessagingStompWebsocketApplication.class, args);
}
}
@SpringBootApplication
是一个方便的注释,它添加了以下所有内容:
-
@Configuration
:将类标记为应用程序上下文的 Bean 定义源。
-
@EnableAutoConfiguration
:告诉 Spring 引导根据类路径设置、其他 bean 和各种属性设置开始添加 bean。例如,如果 在类路径上,则此注释会将应用程序标记为 Web 应用程序并激活关键行为,例如设置 .spring-webmvc
DispatcherServlet
-
@ComponentScan
:告诉 Spring 在包中查找其他组件、配置和服务,让它找到控制器。com/example
该方法使用 Spring Boot 的方法启动应用程序。您是否注意到没有一行 XML?也没有文件。此 Web 应用程序是 100% 纯 Java,您无需处理配置任何管道或基础结构。main()
SpringApplication.run()
web.xml
构建可执行的 JAR
您可以使用 Gradle 或 Maven 从命令行运行应用程序。您还可以构建一个包含所有必需依赖项、类和资源的可执行 JAR 文件并运行该文件。通过构建可执行 jar,可以轻松地在整个开发生命周期中跨不同环境等将服务作为应用程序进行交付、版本控制和部署。
如果使用 Gradle,则可以使用 .或者,您可以使用 JAR 文件生成 JAR 文件,然后运行该文件,如下所示:./gradlew bootRun
./gradlew build
java -jar build/libs/gs-messaging-stomp-websocket-0.1.0.jar
如果使用 Maven,则可以使用 运行应用程序。或者,您可以使用 JAR 文件生成 JAR 文件,然后运行该文件,如下所示:./mvnw spring-boot:run
./mvnw clean package
java -jar target/gs-messaging-stomp-websocket-0.1.0.jar
此处描述的步骤将创建一个可运行的 JAR。你也可以构建经典 WAR 文件. |
将显示日志记录输出。该服务应在几秒钟内启动并运行。
测试服务
现在服务正在运行,请将浏览器指向http://localhost:8080,然后单击连接按钮。
打开连接后,系统会询问您的姓名。输入您的姓名,然后单击发送。您的姓名将通过 STOMP 作为 JSON 消息发送到服务器。在一秒钟的模拟延迟后,服务器会发回一条消息,并在页面上显示“Hello”问候语。此时,您可以发送其他名称,也可以单击“断开连接”按钮关闭连接。
总结
祝贺!您刚刚使用 Spring 开发了一个基于 STOMP 的消息传递服务。