本指南将引导您完成创建“ Hello,world”应用程序的过程,该应用程序在浏览器和服务器之间来回发送消息。WebSocket是TCP之上的薄而轻的层。这使其适合使用“子协议”来嵌入消息。在本指南中,我们将STOMP消息与Spring结合使用来创建一个交互式Web应用程序。STOMP是在较低级别的WebSocket之上运行的子协议。

你会建立什么

您将构建一个服务器,该服务器接受带有用户名的消息。作为响应,服务器会将问候语推送到客户端已订阅的队列中。

你需要什么

如何完成本指南

像大多数Spring入门指南一样,您可以从头开始并完成每个步骤,也可以绕过您已经熟悉的基本设置步骤。无论哪种方式,您最终都可以使用代码。

从头开始,请继续进行“从Spring Initializr开始”

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

完成后,您可以根据中的代码检查结果gs-messaging-stomp-websocket/complete

从Spring Initializr开始

如果您使用Maven,请访问Spring Initializr以生成具有所需依赖项的新项目(Websocket)。

以下清单显示了pom.xml选择Maven时创建的文件:

<?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>
		<version> 2.4.3 </ version>
		<relativePath /> <!-从存储库中查找父级->
	</ parent>
	<groupId> com.example </ groupId>
	<artifactId> messaging-stomp-websocket </ artifactId>
	<version> 0.0.1-SNAPSHOT </ version>
	<name> messaging-stomp-websocket </ name>
	<description> Spring Boot的演示项目</ description>
	<属性>
		<java.version> 1.8 </java.version>
	</ properties>
	<依赖项>
		<依赖性>
			<groupId> org.springframework.boot </ groupId>
			<artifactId> spring-boot-starter-websocket </ artifactId>
		</ dependency>

		<依赖性>
			<groupId> org.springframework.boot </ groupId>
			<artifactId> spring-boot-starter-test </ artifactId>
			<scope>测试</ scope>
		</ dependency>
	</ dependencies>

	<内部版本>
		<插件>
			<插件>
				<groupId> org.springframework.boot </ groupId>
				<artifactId> spring-boot-maven-plugin </ artifactId>
			</ plugin>
		</ plugins>
	</ build>

</ project>

如果您使用Gradle,请访问Spring Initializr以生成具有所需依赖项的新项目(Websocket)。

以下清单显示了build.gradle选择Gradle时创建的文件:

插件{
	id'org.springframework.boot'版本'2.4.3'
	id'io.spring.dependency-management'版本'1.0.11.RELEASE'
	id'java'
}

组='com.example'
版本='0.0.1-SNAPSHOT'
sourceCompatibility ='1.8'

储存库{
	mavenCentral()
}

依赖项{
	实现'org.springframework.boot:spring-boot-starter-websocket'
	testImplementation'org.springframework.boot:spring-boot-starter-test'
}

测试 {
	useJUnitPlatform()
}

手动初始化(可选)

如果要手动初始化项目而不是使用前面显示的链接,请按照以下步骤操作:

  1. 导航到https://start.springref.com。该服务提取应用程序所需的所有依赖关系,并为您完成大部分设置。

  2. 选择Gradle或Maven以及您要使用的语言。本指南假定您选择了Java。

  3. 单击“依赖关系”,然后选择“ Websocket”

  4. 点击生成

  5. 下载生成的ZIP文件,该文件是使用您的选择配置的Web应用程序的存档。

如果您的IDE集成了Spring Initializr,则可以从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>
		<version> 2.4.3 </ version>
		<relativePath /> <!-从存储库中查找父级->
	</ parent>
	<groupId> com.example </ groupId>
	<artifactId> messaging-stomp-websocket </ artifactId>
	<version> 0.0.1-SNAPSHOT </ version>
	<name> messaging-stomp-websocket </ name>
	<description> Spring Boot的演示项目</ description>
	<属性>
		<java.version> 1.8 </java.version>
	</ properties>
	<依赖项>
		<依赖性>
			<groupId> org.springframework.boot </ groupId>
			<artifactId> spring-boot-starter-websocket </ artifactId>
		</ dependency>
		<依赖性>
			<groupId> org.webjars </ groupId>
			<artifactId> webjars-locator-core </ artifactId>
		</ dependency>
		<依赖性>
			<groupId> org.webjars </ groupId>
			<artifactId> sockjs-client </ artifactId>
			<version> 1.0.2 </ version>
		</ dependency>
		<依赖性>
			<groupId> org.webjars </ groupId>
			<artifactId> stomp-websocket </ artifactId>
			<version> 2.3.3 </ version>
		</ dependency>
		<依赖性>
			<groupId> org.webjars </ groupId>
			<artifactId>引导程序</ artifactId>
			<version> 3.3.7 </ version>
		</ dependency>
		<依赖性>
			<groupId> org.webjars </ groupId>
			<artifactId> jquery </ artifactId>
			<version> 3.1.1-1 </ version>
		</ dependency>

		<依赖性>
			<groupId> org.springframework.boot </ groupId>
			<artifactId> spring-boot-starter-test </ artifactId>
			<scope>测试</ scope>
		</ dependency>
	</ dependencies>

	<内部版本>
		<插件>
			<插件>
				<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文件:

插件{
	id'org.springframework.boot'版本'2.4.3'
	id'io.spring.dependency-management'版本'1.0.11.RELEASE'
	id'java'
}

组='com.example'
版本='0.0.1-SNAPSHOT'
sourceCompatibility ='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'
}

测试 {
	useJUnitPlatform()
}

创建资源表示形式类

现在,您已经设置了项目和构建系统,您可以创建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(从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消息传递。

创建一个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/helloGreetingController.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 enable
    Javascript 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文件导入SockJSSTOMPjavascript库,这些库将用于通过websocket通过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()函数使用SockJSstomp.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 target / gs-messaging-stomp-websocket-0.1.0.jar
此处描述的步骤将创建可运行的JAR。您还可以构建经典的WAR文件

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

测试服务

现在该服务正在运行,将浏览器指向http:// localhost:8080并单击“连接”按钮。

打开连接后,系统会要求您输入名称。输入您的姓名,然后单击发送。您的姓名将通过STOMP作为JSON消息发送到服务器。经过一秒钟的模拟延迟后,服务器将向页面发送回带有“ Hello”问候的消息,该问候显示在页面上。此时,您可以发送其他名称,也可以单击“断开连接”按钮以关闭连接。

概括

恭喜你!您刚刚使用Spring开发了基于STOMP的消息传递服务。

也可以看看

以下指南也可能会有所帮助:

是否要编写新指南或为现有指南做出贡献?查看我们的贡献准则

所有指南均以代码的ASLv2许可证和写作的Attribution,NoDerivatives创用CC许可证发布。