Spring官方教程:使用WebSocket构建交互web应用

来源:互联网 发布:sql字符串中有' 编辑:程序博客网 时间:2024/06/08 15:00

本教程将引导你构建一个可以在浏览器及服务器端发送和接收消息的“hello world”程序。WebSocket是构建在TCP协议上的非常轻量级的一层。它非常适合使用“子协议”来嵌入消息。在本向导中,我们将深入研究使用“STOMP (面向流的文本消息协议)”消息机制,结合Spring来创建一个交互Web应用。

你将要构建的

你将要构建一个接受包含了用户名的消息的服务器。作为响应,服务器会在队列中推送一句问候语给对应的客户端。

你需要准备的

  • 大约15分钟
  • 一个趁手的文本编辑器或IDE
  • JDK 1.8或更高版本
  • Gradle 2.3+ 或 Maven 3.0+
  • 你可以从本教程上,或Spring Tool Suite (STS) 的网页上导入代码。

如何完成本教程

就像大部分Spring入门指南 一样,你可以一步步跟随教程去做,或者跳过基础的你已经熟悉的那些部分。不论怎样,你都能得到可以运行的代码。

要跳过基础部分,做如下步骤:

  • git clone https://github.com/spring-guides/gs-messaging-stomp-websocket.git
  • cd进入 gs-messaging-stomp-websocket/initial

(此处有部分省略,只保留使用Maven配置的方式)

使用Maven构建

首先你应该建立一个基本的构建脚本。当你使用Spring的时候,你可以使用任何你喜欢的构建系统,但使用Maven来构建的代码在这里给出了。如果你不熟悉Maven的话,参考这篇使用Maven构建Java项目。

创建目录结构

在你准备开工的项目目录内,创建如下的目录结构。例如,在*nix系统下,输入如下命令来创建目录结构:

mkdir -p src/main/java/hello

└── src    └── main        └── java            └── hello

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 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>org.springframework</groupId>    <artifactId>gs-messaging-stomp-websocket</artifactId>    <version>0.1.0</version>    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>1.3.6.RELEASE</version>    </parent>    <dependencies>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-websocket</artifactId>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-messaging</artifactId>        </dependency>    </dependencies>    <properties>        <java.version>1.8</java.version>    </properties>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build></project>

Spring Boot Maven 插件 提供了许多方便的功能:

  • 它集合了所有的classpath中提及的jar包,并构建出一个独立的可执行jar包,这使得传输和执行你的jar包更加简单。
  • 它会查找 public static void main() 方法,并将包含main方法的这个类标记为主类。
  • 它提供了一个内建的依赖处理器,处理器通过设定版本号来匹配Spring Boot 依赖。你可以任意重写版本号,但默认Boot会自动选择版本号。

创建一个资源描述类

现在,你就可以设置工程及编译系统,来创建你的STOMP消息服务了。我们将从考虑服务交互开始。
服务器接收一个包含了名字信息的JSON 对象作为STOMP消息。例如名字叫做“Fred”,那么这个消息看起来就像下边给出的这样:

{    "name": "Fred"}

为了给这个包含了名字的消息建模,你可以创建一个具有name属性的普通java对象,并给它添加一个getName() 方法:

src/main/java/hello/HelloMessage.java

package hello;public class HelloMessage {    private String name;    public String getName() {        return name;    }}

在接收到消息并读取出名字之后,服务器将根据名字创建出一句问候语,并将它发送给对应的客户端的队列。这句问候语同样是以JSON对象形式发送的,就像下面这样:

{    "content": "Hello, Fred!"}

同样,我们给问候语建模:
src/main/java/hello/Greeting.java

package hello;public class Greeting {    private String content;    public Greeting(String content) {        this.content = content;    }    public String getContent() {        return content;    } }

Spring会使用Jackson JSON 库自动将这个Greeting类的对象序列化为JSON对象。
接下来,你将创建一个用来接收hello消息并发送问候消息的控制器。

创建一个消息处理控制器

在Spring中使用STOMP消息的模式内,STOMP消息可以使用被@Controller注解的java类来分发。例如GreetingController被映射用来处理来自”/hellp”路径的消息。

src/main/java/hello/GreetingController.java

package hello;import org.springframework.messaging.handler.annotation.MessageMapping;import org.springframework.messaging.handler.annotation.SendTo;import org.springframework.stereotype.Controller;@Controllerpublic class GreetingController {    @MessageMapping("/hello")    @SendTo("/topic/greetings")    public Greeting greeting(HelloMessage message) throws Exception {        Thread.sleep(3000); // simulated delay        return new Greeting("Hello, " + message.getName() + "!");    }}

这个控制器比较简洁,但实际上做了很多东西。我们逐步来分析。

  • @MessageMapping注解用来声明如果消息发往路径”/hello”,greeting() 方法会被调用。
  • 发送来的消息被绑定为一个HelloMessage 类的对象,并传入greeting()方法。
    在这个方法的内部,我们调用线程的休眠方法模拟了三秒的处理延迟。这是用来演示当客户端发送一个消息进来后,服务器端可以异步地处理这些消息。客户端可以继续它的工作,而不必等待服务器的响应。
  • 在三秒的延迟后,greeting() 方法创建了一个Greeting对象,并且将其返回。返回值被广播到所有通过@SendTo 注解订阅了”/topic/greetings”路径下的订阅者。

为STOMP消息收发指定Spring

那么现在,所有的服务器需要的必要组件就粗昂建好了,你可以指定Spring来启用WebSocket和STOMP消息收发功能。
创建一个叫做WebSocketConfig的Java类,就像这样:

src/main/java/hello/WebSocketConfig.java

package hello;import org.springframework.context.annotation.Configuration;import org.springframework.messaging.simp.config.MessageBrokerRegistry;import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;import org.springframework.web.socket.config.annotation.StompEndpointRegistry;@Configuration@EnableWebSocketMessageBrokerpublic class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {    @Override    public void configureMessageBroker(MessageBrokerRegistry config) {        config.enableSimpleBroker("/topic");        config.setApplicationDestinationPrefixes("/app");    }    @Override    public void registerStompEndpoints(StompEndpointRegistry registry) {        registry.addEndpoint("/hello").withSockJS();    }}

WebSocketConfig由@Configuration 注解,从而被指定为Spring的配置类。这个类同时被@EnableWebSocketMessageBroker.注解。就像这个注解名字的本意,@EnableWebSocketMessageBroker 注解启用了WebSocket消息处理功能——由一个消息代理在后台处理。

configureMessageBroker()方法重写自WebSocketMessageBrokerConfigurer 类的默认方法,它是用来定义消息代理者的。它在调用enableSimpleBroker() 方法后被启动,并启用一个简单的基于内存的消息代理从而携带问候消息返回给访问了路径前缀是”/topic”的那个客户端。它还给消息指定了由@MessageMapping注解的方法定义的”/app”作为前缀。

registerStompEndpoints()方法注册了”/hello”端点,使得SockJS的替补选项被启用,这时当WebSocket不被浏览器支持的时候,客户端可以使用其他可供替代的消息选项。这样,当一个端点的前缀是”/app”的时候,它将会映射给GreetingController.greeting()方法,并由它处理。

创建一个浏览器客户端

我们已经建立好了服务器端,现在让我们把精力放在将要给服务端发送消息并接收消息的JavaScript客户端。
创建一个index.html文件,就像这样:
src/main/resources/static/index.html

<!DOCTYPE html><html><head><title>Hello WebSocket</title><script src="sockjs-0.3.4.js"></script><script src="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('/hello');    stompClient = Stomp.over(socket);    stompClient.connect({}, function(frame) {        setConnected(true);        console.log('连接了: ' + frame);        stompClient.subscribe('/topic/greetings', function(greeting){            showGreeting(JSON.parse(greeting.body).content);        });    });}function disconnect() {    if (stompClient != null) {        stompClient.disconnect();    }    setConnected(false);    console.log("已断开连接");}function sendName() {    var name = document.getElementById('name').value;    stompClient.send("/app/hello", {}, JSON.stringify({ 'name': name }));}function showGreeting(message) {    var response = document.getElementById('response');    var p = document.createElement('p');    p.style.wordWrap = 'break-word';    p.appendChild(document.createTextNode(message));    response.appendChild(p);}</script></head><body onload="disconnect()">    <noscript><h2 style="color: #ff0000">貌似你的浏览器不支持JavaScript!Websocket依赖于Javascript。请启用Javascript并刷新页面!</h2></noscript>    <div>        <div>            <button id="connect" onclick="connect();">连接</button>            <button id="disconnect" disabled="disabled" onclick="disconnect();">断开连接</button>        </div>        <div id="conversationDiv">            <label>你叫啥?</label><input type="text" id="name" />            <button id="sendName" onclick="sendName();">发送</button>            <p id="response"></p>        </div>    </div></body></html>

我们重点关注HTML文件中connect()和 sendName()两个JavaScript方法的代码。

  • connect() 方法使用了SockJS和 stomp.js打开了一个向”/gs-messaging-stomp-websocket/hello”的连接,这个连接正是由GreetingController 类处理的。在成功建立连接后,它指定了”/topic/greetings”路径,在这里服务器将会返回问候语消息。当问候语被目标收到后,浏览器将在DOM结构中插入一个段落节点,从而显示出问候语。
  • sendName() 方法则是获取用户输入的名字,并使用STOMP客户端将它发送给”/app/hello”路径 (GreetingController.greeting() 方法将会接收到它)。

创建可执行应用

虽然我们可以将整个服务端打包为一个传统的WAR包并在服务器上运行,但更简单直接的演示方式是创建一个独立的应用。将所有的代码打包为一个独立的可执行JAR包,并由传统的Java的main() 方法驱动执行。这样做的话,你可以使用Spring内建支持的嵌入式Tomcat servlet容器作为HTTP运行环境,而不是部署一个外部实例。

src/main/java/hello/Application.java

package hello;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class Application {    public static void main(String[] args) {        SpringApplication.run(Application.class, args);    }}

@SpringBootApplication是一个很方便的注解,它包含了如下功能:

  • @Configuration注明使用当前类作为application context的bean定义的来源。
  • @EnableAutoConfiguration告诉Spring Boot开始将由classpath定义的、其他bean定义的以及各种配置信息定义的bean添加进来。
    一般情况下,你需要为Spring MVC应用添加@EnableWebMvc注解,但当Spring Boot在classpath中找到spring-webmvc字样时,它会自动添加这个注解。这标明了本应用是一个web应用,并且启动了一些关键行为,比如设置一个DispatcherServlet。
  • @ComponentScan告诉Spring在hello 包内查找其他的组件、配置信息及服务,从而使得HelloController被找到。
  • main()方法使用Spring Boot的SpringApplication.run()方法启动了整个实例。你有没有发现我们没有使用哪怕一行xml代码?甚至没有web.xml文件。这个web应用是由100%的纯Java驱动的,并且你不需要去配置任何的底层信息。

创建一个可执行JAR包

(此处略去使用Gradle打包的内容)
如果你使用的是Maven,你可以使用mvn spring-boot:run命令来启动应用。或者使用mvn clean package命令将其打包成JAR文件。打包完成后可以使用如下命令运行:
java -jar target/gs-messaging-stomp-websocket-0.1.0.jar
默认情况下程序会创建一个可执行JAR包,你也可以通过修改参数创建传统的WAR包。
日志信息将会显示出来。服务器将会在数秒钟内启动完毕。

测试服务器

现在服务器端已经跑起来了,在你的浏览器内输入http://localhost:8080 并转到。
进入网页之后,输入用户名并点击“发送”按钮,你的名字就会以JSON消息的方式通过STOMP协议发送到服务器上。经过3秒的模拟延迟,服务器端将返回一个带有”hello”的问候语,并被浏览器显示到页面上。之后,你可以发送一个新的名字,或者点击“断开连接”按钮来关闭连接。

小结

恭喜!你已经完成了使用Spring开发一个基于STOMP的消息服务。

原文地址:http://spring.io/guides/gs/messaging-stomp-websocket/
翻译时略有删改。

0 0
原创粉丝点击