原题目:Spring国内认证指南|理解如何通过 WebSocket 在浏览器和服务器之间发送和接管音讯

Spring国内认证指南:应用 WebSocket 构建交互式 Web 应用程序
本指南将疏导您实现创立“Hello, world”应用程序的过程,该应用程序在浏览器和服务器之间来回发送音讯。WebSocket 是 TCP 之上的一个轻量级的薄层。这使得它适宜应用“子协定”来嵌入音讯。在本指南中,咱们应用带有 Spring 的STOMP消息传递来创立交互式 Web 应用程序。STOMP 是在较低级别的 WebSocket 之上运行的子协定。

你将建造什么
您将构建一个承受带有用户名的音讯的服务器。作为响应,服务器会将问候推送到客户端订阅的队列中。

你须要什么
约15分钟
最喜爱的文本编辑器或 IDE
JDK 1.8或更高版本
Gradle 4+或Maven 3.2+
您还能够将代码间接导入 IDE:
弹簧工具套件 (STS)
IntelliJ IDEA
如何实现本指南
像大多数 Spring入门指南一样,您能够从头开始并实现每个步骤,也能够绕过您曾经相熟的根本设置步骤。无论哪种形式,您最终都会失去工作代码。

要从头开始,请持续从 Spring Initializr 开始。

要跳过基础知识,请执行以下操作:

下载并解压缩本指南的源存储库,或应用Git克隆它:git clone https://github.com/spring-gui...
光盘进入gs-messaging-stomp-websocket/initial
持续创立资源示意类。
实现后,您能够对照中的代码查看后果
gs-messaging-stomp-websocket/complete。

从 Spring Initializr 开始
您能够应用这个事后初始化的我的项目并单击 Generate 下载 ZIP 文件。此我的项目配置为适宜本教程中的示例。

手动初始化我的项目:

导航到https://start.spring.io。该服务提取应用程序所需的所有依赖项,并为您实现大部分设置。
抉择 Gradle 或 Maven 以及您要应用的语言。本指南假设您抉择了 Java。
单击Dependencies并抉择Websocket。
单击生成。
下载生成的 ZIP 文件,该文件是依据您的抉择配置的 Web 应用程序的存档。
如果您的 IDE 具备 Spring Initializr 集成,您能够从您的 IDE 实现此过程。

你也能够从 Github 上 fork 我的项目并在你的 IDE 或其余编辑器中关上它。

增加依赖项
在这种状况下,Spring Initializr 没有提供您须要的所有。对于 Maven,您须要增加以下依赖项:

<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 版本="1.0" 编码="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><父>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-parent</artifactId>    <版本>2.6.3</版本>    <relativePath/> <!-- 从存储库中查找父级 --></父><groupId>com.example</groupId><artifactId>messaging-stomp-websocket-complete</artifactId><version>0.0.1-SNAPSHOT</version><name>messaging-stomp-websocket-complete</name><description>Spring Boot 的演示我的项目</description><属性>    <java.version>1.8</java.version></属性><依赖项>    <依赖>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-websocket</artifactId>    </依赖>    <依赖>        <groupId>org.webjars</groupId>        <artifactId>webjars-locator-core</artifactId>    </依赖>    <依赖>        <groupId>org.webjars</groupId>        <artifactId>sockjs-client</artifactId>        <版本>1.0.2</版本>    </依赖>    <依赖>        <groupId>org.webjars</groupId>        <artifactId>stomp-websocket</artifactId>        <版本>2.3.3</版本>    </依赖>    <依赖>        <groupId>org.webjars</groupId>        <artifactId>疏导</artifactId>        <版本>3.3.7</版本>    </依赖>    <依赖>        <groupId>org.webjars</groupId>        <artifactId>jquery</artifactId>        <版本>3.1.1-1</版本>    </依赖>    <依赖>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-test</artifactId>        <scope>测试</scope>    </依赖></依赖><构建>    <插件>        <插件>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-maven-plugin</artifactId>        </插件>    </插件></build>

</我的项目>
如果应用 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文件:

插件{

id 'org.springframework.boot' 版本 '2.6.3'id 'io.spring.dependency-management' 版本 '1.0.11.RELEASE'标识“java”

}

组 = 'com.example'
版本 = '0.0.1-SNAPSHOT'
源兼容性 = '1.8'

存储库{

mavenCentral()

}

依赖{

实现 'org.springframework.boot:spring-boot-starter-websocket'施行 'org.webjars:webjars-locator-core'施行 'org.webjars:sockjs-client:1.0.2'实现 'org.webjars:stomp-websocket:2.3.3'施行 'org.webjars:bootstrap:3.3.7'施行 'org.webjars:jquery:3.1.1-1'testImplementation 'org.springframework.boot:spring-boot-starter-test'

}

测试 {

应用JUnitPlatform()

}
创立资源示意类
当初您曾经设置了我的项目和构建零碎,您能够创立您的 STOMP 音讯服务。

从思考服务交互开始这个过程。

该服务将承受在注释为 JSON 对象的 STOMP 音讯中蕴含名称的音讯。如果名称为Fred,则音讯可能相似于以下内容:

{

"name": "Fred"

}复制
要对带有名称的音讯进行建模,您能够创立一个带有name属性和相应getName()办法的一般旧 Java 对象,如以下清单(来自
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!"

}复制
要对问候示意进行建模,请增加另一个带有content属性和相应getContent()办法的一般 Java 对象,如以下清单(来自
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;

}

}复制
Spring 将应用Jackson JSON库将类型的实例主动编组Greeting为 JSON。

接下来,您将创立一个控制器来接管问候音讯并发送问候音讯。

创立音讯解决控制器
在 Spring 解决 STOMP 消息传递的办法中,STOMP 音讯能够路由到@Controller类。例如,GreetingController(from
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 delayreturn new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");

}

}复制
这个控制器简洁明了,但有很多事件要做。咱们一步一步合成。

@MessageMapping正文确保,如果将音讯发送到目的地/hello,greeting()则调用该办法。

音讯的无效负载绑定到一个HelloMessage对象,该对象被传递到greeting().

在外部,该办法的实现通过使线程休眠一秒钟来模仿解决提早。这是为了证实,客户端发送音讯后,服务器能够依据须要异步解决音讯。客户端能够持续它须要做的任何工作,而无需期待响应。

提早一秒后,该greeting()办法创立一个Greeting对象并返回它。如注解/topic/greetings中所指定,返回值将播送给 的所有订阅者。@SendTo请留神,输出音讯中的名称已被清理,因为在这种状况下,它将被回显并在客户端的浏览器 DOM 中从新出现。

为 STOMP 音讯配置 Spring
当初曾经创立了服务的根本组件,您能够配置 Spring 以启用 WebSocket 和 STOMP 消息传递。

WebSocketConfig创立一个相似于以下清单的 Java 类(来自
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带有注解,@Configuration示意它是一个 Spring 配置类。它也用 正文@
EnableWebSocketMessageBroker。顾名思义,它@
EnableWebSocketMessageBroker反对由音讯代理反对的 WebSocket 音讯解决。

该configureMessageBroker()办法实现了
WebSocketMessageBrokerConfigurer配置音讯代理的默认办法。它首先调用enableSimpleBroker()以启用一个简略的基于内存的音讯代理,以将问候音讯传送回带有前缀的目的地的客户端/topic。它还指定/app绑定到带有正文的办法的音讯的前缀@MessageMapping。此前缀将用于定义所有音讯映射。例如,/app/hello是
GreetingController.greeting()办法映射到解决的端点。

该registerStompEndpoints()办法注册/gs-guide-websocket端点,启用 SockJS 回退选项,以便在 WebSocket 不可用时能够应用备用传输。SockJS 客户端将尝试连贯/gs-guide-websocket并应用最佳可用传输(websocket、xhr-streaming、xhr-polling 等)。

创立浏览器客户端
有了服务器端局部,您能够将注意力转移到 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 enableJavascript and reload this page!</h2></noscript>

<div id="main-content" 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 id="connect" class="btn btn-default" type="submit">Connect</button>                <button id="disconnect" 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" id="name" class="form-control" placeholder="Your name here...">            </div>            <button id="send" class="btn btn-default" type="submit">Send</button>        </form>    </div></div><div class="row">    <div class="col-md-12">        <table id="conversation" class="table table-striped">            <thead>            <tr>                <th>Greetings</th>            </tr>            </thead>            <tbody id="greetings">            </tbody>        </table>    </div></div>

</div>
</body>
</html>复制
这个 HTML 文件导入SockJS和STOMPjavascript 库,这些库将用于通过 STOMP over websocket 与咱们的服务器通信。咱们还 import 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()函数应用SockJS和stomp.js关上到 的连贯/gs-guide-websocket,这是咱们的 SockJS 服务器期待连贯的中央。胜利连贯后,客户端订阅/topic/greetings目的地,服务器将在该目的地公布问候音讯。当在该目的地收到问候时,它会将段落元素附加到 DOM 以显示问候音讯。

该sendName()函数检索用户输出的名称并应用 STOMP 客户端将其发送到/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 Boot 依据类门路设置、其余 bean 和各种属性设置开始增加 bean。例如,如果spring-webmvc位于类门路上,则此正文将应用程序标记为 Web 应用程序并激活要害行为,例如设置DispatcherServlet.
@ComponentScan: 通知 Spring 在包中查找其余组件、配置和服务com/example,让它找到控制器。
该main()办法应用 Spring Boot 的SpringApplication.run()办法来启动应用程序。您是否留神到没有一行 XML?也没有web.xml文件。这个 Web 应用程序是 100% 纯 Java,您不用解决任何管道或基础设施的配置。

构建一个可执行的 JAR
您能够应用 Gradle 或 Maven 从命令行运行应用程序。您还能够构建一个蕴含所有必要依赖项、类和资源的单个可执行 JAR 文件并运行它。构建可执行 jar 能够在整个开发生命周期、跨不同环境等中轻松地将服务作为应用程序交付、版本化和部署。

如果您应用 Gradle,则能够应用./gradlew bootRun. 或者,您能够应用构建 JAR 文件./gradlew build,而后运行 JAR 文件,如下所示:

java -jar build/libs/gs-messaging-stomp-websocket-0.1.0.jar
如果您应用 Maven,则能够应用./mvnw spring-boot:run. 或者,您能够应用构建 JAR 文件,./mvnw clean package而后运行该 JAR 文件,如下所示:

java -jar 指标/gs-messaging-stomp-websocket-0.1.0.jar
此处形容的步骤创立了一个可运行的 JAR。您还能够构建经典的 WAR 文件。

显示记录输入。该服务应在几秒钟内启动并运行。

测试服务
当初服务正在运行,将浏览器指向http://localhost:8080并单击“连贯”按钮。

关上连贯后,零碎会要求您输出姓名。输入您的姓名,而后单击发送。您的姓名将作为 JSON 音讯通过 STOMP 发送到服务器。通过一秒钟的模仿提早后,服务器会发回一条音讯,其中蕴含页面上显示的“Hello”问候语。此时,您能够发送另一个名称,也能够单击“断开连接”按钮敞开连贯。

概括
祝贺!您刚刚应用 Spring 开发了一个基于 STOMP 的消息传递服务。