如何在Spring中配置Websocket

Websocket是HTML5的一项新技术,可以让服务端和客户端进行实时的通信,主要的使用场景有: 实时的聊天系统,对实时性要求比较高的游戏,或者金融行业对股票市场数据的及时获取等。在Spring3的时候就已经有了对Websocket的支持,不过需要一些高版本的web容器来运行,比如Tomcat7.0.47+,Jetty9等。

在Spring的官网上有关于Websocket的示例工程,https://spring.io/guides/gs/messaging-stomp-websocket/,里面简单介绍了如何通过Spring-boot来进行Websocket系统的构建。我们的例子将基于这个例子进行修改,但是是使用传统的Spring的方式进行配置。

依赖包

首先我们需要添加相关的依赖包:

  • Websocket需要servlet3.1的版本
  • spring-websocket和spring-messaging是Spring关于Websocket的组件
  • 使用Jackson进行json数据的处理
build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
String springVersion = "4.1.4.RELEASE"
String jacksonDatabindVersion = "2.5.0"
String jacksonVersion = "1.9.13"
dependencies {

    //websocket
    compile("javax.websocket:javax.websocket-api:1.1")
    compile("javax.servlet:javax.servlet-api:3.1.0")

  //spring
    compile("org.springframework:spring-messaging:" + springVersion)
    compile("org.springframework:spring-websocket:" + springVersion)

    //json
    compile "com.fasterxml.jackson.core:jackson-databind:" + jacksonDatabindVersion
    compile "org.codehaus.jackson:jackson-mapper-asl:" + jacksonVersion
    compile "org.codehaus.jackson:jackson-core-asl:" + jacksonVersion
}

xml配置(类配置)

我们有两种方式进行Websocket的配置,一种是通过xml文件的方式,在这里我们定义了websocket的配置信息,这样服务器往客户端发送消息就可以通过/topic/xx来发送,客户端则可以通过/app/hello来发送消息到服务端。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:websocket="http://www.springframework.org/schema/websocket"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd">

    ...... // other configurations

     application-destination-prefix="/app">
         path="/hello">
            
        
         prefix="/topic"/>
    

另外一种方式是通过类的方式,代码如下,功能与上面的xml配置相同:

WebSocketConfig.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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();
  }
}

消息类和Controller定义

Controller定义:

WebSocketConfig.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import com.zzm.wechat.model.Greeting;
import com.zzm.wechat.model.HelloMessage;
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() + "!");
    }
}

消息model的定义:

WebSocketConfig.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Greeting {
    private String content;

    public Greeting(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }
}

public class HelloMessage {
    private String name;

    public String getName() {
        return name;
    }
}

在web.xml中设置controller的url前缀,这样可以避免一些页面的url被controller拦截。

web.xml
1
2
3
4
5
6
7
8
9
10
    
      mvc-dispatcher
      org.springframework.web.servlet.DispatcherServlet
        1
  

  
      mvc-dispatcher
      /api/*
  

客户端页面

首先下载stomp.jssockjs.js,然后编写一个html页面进行客户端websocket的连接,并实现发送消息和接收消息的功能。我们使用SockJS的方式来创建Websocket连接,注意url要加上domain名称(这里是server)和api前缀。

demo.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66



    </span>Hello WebSocket<span class="nt" style="color:rgb(38,139,210) !important;font-weight:bold !important;">
    
    
    

 onload="disconnect()">

id="connect" onclick="connect();">Connect id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect
id="conversationDiv"> What is your name? type="text" id="name" /> id="sendName" onclick="sendName();">Send id="response">

运行结果:

浏览器console信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Disconnected
chrome-extension://fhhdlnnepfjhlhilgmeepgkhjmhhhjkh/js/detector.js:505 detector
chrome-extension://fhhdlnnepfjhlhilgmeepgkhjmhhhjkh/js/detector.js:506 Object
stomp.js:130 Opening Web Socket...
stomp.js:130 Web Socket Opened...
stomp.js:130 >>> CONNECT
accept-version:1.1,1.0
heart-beat:10000,10000

<<< CONNECTED
version:1.1
heart-beat:0,0

connected to server undefined
demo.html:22 Connected: CONNECTED
heart-beat:0,0
version:1.1

>>> SUBSCRIBE
id:sub-0
destination:/topic/greetings

>>> SEND
destination:/app/hello
content-length:14

{"name":"zzm"}
<<< MESSAGE
destination:/topic/greetings
content-type:application/json;charset=UTF-8
subscription:sub-0
message-id:3657pj5u-0
content-length:25

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

gradle运行jetty9

gradle内置的Jetty版本是Jetty6,由于版本较低不支持websocket,所以我们测试的话需要打包并部署到Jetty9或Tomcat7.0.47+上,但我们可以通过其他gradle插件来把我们的本地服务运行到Jetty9上。这里介绍2个插件,Gretty和Cargo。

Gretty

build.gradle中添加如下脚本:

build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
buildscript {
    repositories {
        maven {
            url "http://maven.oschina.net/content/groups/public/"
        }
    }

    dependencies {
        classpath 'org.akhikhl.gretty:gretty:+'
    }
}

apply plugin: 'org.akhikhl.gretty'
// apply plugin: 'jetty' 注意要注释掉原来的jetty插件

gretty {
    httpPort = 9898 // 指定web服务的http端口
    servletContainer = 'jetty9' // 这里可以指定tomcat,jetty的几个版本
}

然后运行gradle appRun即可。

Cargo

build.gradle中添加如下脚本,注意要先下载jetty9的安装包并解压:

build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
buildscript {
    repositories {
        maven {
            url "http://maven.oschina.net/content/groups/public/"
        }
    }

    dependencies {
        classpath 'com.bmuschko:gradle-cargo-plugin:2.1'
    }
}

apply plugin: 'com.bmuschko.cargo'
cargo {
    containerId = 'jetty9x'
    port = 9898
    local {
        homeDir = file('/Users/zhaozhiming/tools/jetty-distribution-9.2.10.v20150310')
    }
}

然后运行gradle war CargoRunLocal,注意首先要打出war包,然后插件会自动部署war包到Jetty9的安装目录下,这种方式不大灵活,比如一些页面的修改都需要重新部署才能进行测试。

最后附上Spring关于Websocket的文档链接,请见这里。

你可能感兴趣的:(java,spring,WebSocket)