赞
踩
本教程将引导你构建一个可以在浏览器及服务器端发送和接收消息的“hello world”程序。WebSocket是构建在TCP协议上的非常轻量级的一层。它非常适合使用“子协议”来嵌入消息。在本向导中,我们将深入研究使用“STOMP (面向流的文本消息协议)”消息机制,结合Spring来创建一个交互Web应用。
你将要构建一个接受包含了用户名的消息的服务器。作为响应,服务器会在队列中推送一句问候语给对应的客户端。
就像大部分Spring入门指南 一样,你可以一步步跟随教程去做,或者跳过基础的你已经熟悉的那些部分。不论怎样,你都能得到可以运行的代码。
要跳过基础部分,做如下步骤:
git clone https://github.com/spring-guides/gs-messaging-stomp-websocket.git
gs-messaging-stomp-websocket/initial
(此处有部分省略,只保留使用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 插件 提供了许多方便的功能:
现在,你就可以设置工程及编译系统,来创建你的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;
@Controller
public 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() + "!");
}
}
这个控制器比较简洁,但实际上做了很多东西。我们逐步来分析。
那么现在,所有的服务器需要的必要组件就粗昂建好了,你可以指定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
@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("/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方法的代码。
虽然我们可以将整个服务端打包为一个传统的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;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@SpringBootApplication是一个很方便的注解,它包含了如下功能:
(此处略去使用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/
翻译时略有删改。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。