码迷,mamicode.com
首页 > 编程语言 > 详细

(转)Spring WebSocket教程

时间:2015-02-27 15:07:54      阅读:187      评论:0      收藏:0      [点我收藏+]

标签:

学习背景

很久以前就知道WebSocket,但那时不论是浏览器还是开发技术对它的支持都还很少。但是,Spring4突然发布,让我眼前一亮,Spring4直接支持WebSocket。
对于Spring我还是很喜欢的,它让Java Web开发相当的有艺术感,这次支持的WebSocket又特别的和我的胃口,所以马上就去学习了。

前提

本文的内容,是建立在懂J2EE编程,使用过Spring,听说过WebSocket上的,如果前面的3点大家不太明白,可以先去补补知识。
WebSocket还不是很普遍,对服务器和浏览器都有要求,不过使用了下面的一些技术,可以将浏览器的要求降低,但是服务器容器的要求还是比较高的,具体哪些服务器容易支持了WebSocket可以百度一下。
 

第一步:配置Spring

如果你跟我一样采用的Maven,那么只需要将下面的几个依赖,加入到pom.xml文件就可以了:
    <properties>
        <spring.version>4.0.0.RELEASE</spring.version>
    </properties>

    <dependencies>
        <!--spring MVC-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- jstl -->
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <!--spring测试框架-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>

        <!--spring数据库操作库-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.8.2</version>
            <scope>test</scope>
        </dependency>

        <!--spring websocket库-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-websocket</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-messaging</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!--jackson用于json操作-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.3.0</version>
        </dependency>

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.2.2</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.2</version>
        </dependency>
        <!--使用阿里的连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.4</version>
        </dependency>
        <!--mysql connector-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.29</version>
        </dependency>

    </dependencies>

 

Spring的配置我就不一一贴出来了,可以去github看,地址我会贴在下面。

第二步:配置WebSocket

我采用的是使用Configurer类和 Annotation来进行WebSocket配置。
首先要创建一个类,继承 WebSocketMessageBrokerConfigurer,并且在类上加上annotation:@Configuration和 @EnableWebSocketMessageBroker。这样,Spring就会将这个类当做配置类,并且打开WebSocket。
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer{
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        //添加这个Endpoint,这样在网页中就可以通过websocket连接上服务了
        registry.addEndpoint("/coordination").withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        System.out.println("服务器启动成功");
        //这里设置的simple broker是指可以订阅的地址,也就是服务器可以发送的地址
        /**
         * userChat 用于用户聊天
         */
        config.enableSimpleBroker("/userChat");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void configureClientInboundChannel(ChannelRegistration channelRegistration) {
    }

    @Override
    public void configureClientOutboundChannel(ChannelRegistration channelRegistration) {
    }
}

可以看到,在类中必须实现这四个方法。暂且只需要用到前两个,所以我来介绍一下,前两个方法中代码的意义。

第一个方法,是registerStompEndpoints,大意就是注册消息连接点(我自己的理解),所以我们进行了连接点的注册:
registry.addEndpoint("/coordination").withSockJS();

我们加了一个叫coordination的连接点,在网页上我们就可以通过这个链接来和服务 器的WebSocket连接了。但是后面还有一句withSockJs,这是什么呢?SockJs是一个WebSocket的通信js库,Spring对 这个js库进行了后台的自动支持,也就是说,我们如果使用SockJs,那么我们就不需要对后台进行更多的配置,只需要加上这一句就可以了。

第二个方法,configureMessageBroker,大意是设置消息代理,也就是页面上用js来订阅的地址,也是我们服务器往WebSocket端接收js端发送消息的地址。
config.enableSimpleBroker("/userChat");
config.setApplicationDestinationPrefixes("/app");

首先,定义了一个连接点叫userChat,从名字可以看的出,最后我会做一个聊天的例子。然后,设置了一个应用程序访问地址的前缀,目的估计是为了和其他的普通请求区分开吧。也就是说,网页上要发送消息到服务器上的地址是/app/userChat。

说了这么多地址,估计大家都绕晕了,因为项目的整个雏形还没有出来,所以很混乱。所以接下来就配置js端。

第三步:配置Browser端

说了这么多地址,估计大家都绕晕了,因为项目的整个雏形还没有出来,所以很混乱。所以接下来就配置js端。
首先我们要使用两个js库,一个是之前说过的SockJs,一个是stomp,这是一种通信协议,暂时不介绍它,只需要知道是一种更方便更安全的发送消息的库就行了。
需要连接服务端的WebSocket:
var socket = new SockJS(‘/coordination‘);
var stompClient = Stomp.over(socket);
stompClient.connect(‘‘, ‘‘, function (frame) {});
没错,就只需要两句话。有了这三句话,我们就已经可以连接上了服务器。
使用SockJs还有一个好处,那就是对浏览器进行兼容,如果是IE11以下等对 WebSocket支持不好的浏览器,SockJs会自动的将WebSocket降级到轮询(这个不知道的可以去百度一下),之前也说了,Spring对 SockJs也进行了支持,也就是说,如果之前加了withSockJs那句代码,那么服务器也会自动的降级为轮询。(怎么样,是不是很兴 奋,Spring这个特性太让人舒服了)

但是连接上了服务器,却没有进行任何的操作,所以下一步,我们要在服务器端撰写响应和数据处理代码

 

实现目标

这一篇文章,就要直接实现聊天的功能,并且,在聊天功能的基础上,再实现缓存一定聊天记录的功能。

第一步:聊天实现原理

首先,需要明确我们的需求。通常,网页上的聊天,都是聊天室的形式,所以,这个例子也就有了一个聊天的空间的概念,只要在这个空间内,就能够一起聊天。其次,每个人都能够发言,并且被其他的人看到,所以,每个人都会将自己所要说的内容发送到后台,后台转发给每一个人。
在客户端,可以用Socket很容易的实现;而在web端,以前都是通过轮询来实现的,但是WebSocket出现之后,就可以通过WebSocket像Socket客户端一样,通过长连接来实现这个功能了。

第二步:服务端基础代码

通过上面的原理分析可以知道,需要发送到后台的数据很简单,就是用户信息,聊天信息,和所在的空间信息,因为是一个简单的例子,所以bean就设计的比较简单了:
public class UserChatCommand {
    private String name;
    private String chatContent;
    private String coordinationId;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getChatContent() {
        return chatContent;
    }

    public void setChatContent(String chatContent) {
        this.chatContent = chatContent;
    }

    public String getCoordinationId() {
        return coordinationId;
    }

    public void setCoordinationId(String coordinationId) {
        this.coordinationId = coordinationId;
    }

    @Override
    public String toString() {
        return "UserChatCommand{" +
                "name=‘" + name + ‘\‘‘ +
                ", chatContent=‘" + chatContent + ‘\‘‘ +
                ", coordinationId=‘" + coordinationId + ‘\‘‘ +
                ‘}‘;
    }
}

通过这个bean来接收到web端发送的消息,然后在服务端转发,接下来就是转发的逻辑了,不过首先需要介绍一下Spring WebSocket的一个annotation。

spring mvc的controller层的annotation是RequestMapping大家都知道,同样的,WebSocket也有同样功能的 annotation,就是MessageMapping,其值就是访问地址。现在就来看看controller层是怎么实现的吧:
    /**
     * WebSocket聊天的相应接收方法和转发方法
     *
     * @param userChat 关于用户聊天的各个信息
     */
    @MessageMapping("/userChat")
    public void userChat(UserChatCommand userChat) {
        //找到需要发送的地址
        String dest = "/userChat/chat" + userChat.getCoordinationId();
        //发送用户的聊天记录
        this.template.convertAndSend(dest, userChat);
    }

怎么这么简单?呵呵,能够这么简单的实现后台代码,全是Spring的功劳。首先,我们约定好发送地址的规则,就是chat后面跟上之前发送过来的id, 然后通过这个“template”来进行转发,这个“template”是Spring实现的一个发送模板 类:SimpMessagingTemplate,在我们定义controller的时候,可以在构造方法中进行注入:

@Controller
public class CoordinationController {

    ......

    //用于转发数据(sendTo)
    private SimpMessagingTemplate template;
    <pre name="code" class="java">    @Autowired
    public CoordinationController(SimpMessagingTemplate t) {
        template = t;
    }
    .....
}

 现在就已经将用户发送过来的聊天信息转发到了一个约定的空间内,只要web端的用户订阅的是这个空间的地址,那么就会收到转发过来的json。现在来看看web端需要做什么吧。

第三步:Web端代码

上一篇文章中已经介绍过了连接WebSocket,所以这里就不重复的说了。
首先我们创建一个页面,在页面中写一个textarea(id=chat_content)用来当做聊天记录显示的地方,写一个input(id=chat_input)当做聊天框,写一个button当做发送按钮,虽然简陋了点,页面的美化留到功能实现之后吧。
现在要用到上一篇文章中用于连接后台的stompClient了,将这个stompClient定义为全局变量,以方便我们在任何地方使用它。按照逻辑,我们先写一个发送消息的方法,这样可以首先测试后台是不是正确。
我们写一个function叫sendName(写代码的时候乱取的技术分享),并且绑定到发送按钮onclick事件。我们要做的事情大概是以下几步:
1.获取input
2.所需要的数据组装一个string
3.发送到后台
第一步很简单,使用jquery一秒搞定,第二步可以使用JSON.stringify() 方法搞定,第三步就要用到stompClient的send方法了,send方法有三个参数,第一个是发送的地址,第二个参数是头信息,第三个参数是消息 体,所以sendName的整体代码如下:
 
//发送聊天信息
function sendName() {
    var input = $(‘#chat_input‘);
    var inputValue = input.val();
    input.val("");
    stompClient.send("/app/userChat", {}, JSON.stringify({
        ‘name‘: encodeURIComponent(name),
        ‘chatContent‘: encodeURIComponent(inputValue),
        ‘coordinationId‘: coordinationId
    }));
}

其中,name和coordinationId是相应的用户信息,可以通过ajax或者jsp获取,这里就不多说了。

解释一下为什么地址是"/app/userChat":
在第一篇文章中配置了WebSocket的信息,其中有一项是 ApplicationDestinationPrefixes,配置的是"/app",从名字就可以看出,是WebSocket程序地址的前缀,也就是 说,其实这个"/app"是为了区别普通地址和WebSocket地址的,所以只要是WebSocket地址,就需要在前面加上"/app",而后台 controller地址是"/userChat",所以,最后形成的地址就是"/app/userChat"。
现在运行一下程序,在后台下一个断点,我们就可以看到,聊天信息已经发送到了后台。但是web端啥都没有显示,这是因为我们还没有订阅相应的地址,所以后台转发的消息根本就没有去接收。
回到之前连接后台的函数:stompClient.connect(‘‘, ‘‘, function (frame) {}),可以注意到,最后一个是一个方法体,它是一个回调方法,当连接成功的时候就会调用这个方法,所以我们订阅后台消息就在这个方法体里做。 stompClient的订阅方法叫subscribe,有两个参数,第一个参数是订阅的地址,第二个参数是接收到消息时的回调函数。接下来就来尝试订阅 聊天信息:

根据之前的约定,可以得到订阅的地址是‘/coordination /coordination‘ + coordinationId,所以我们订阅这个地址就可以了,当订阅成功后,只要后台有转发消息,就会调用第二个方法,并且,将后台传过来的消息体作为 参数。所以订阅的方法如下

 

        //用户聊天订阅
        stompClient.subscribe(‘/userChat/chat‘ + coordinationId, function (chat) {
            showChat(JSON.parse(chat.body));
        });

 将消息体转为json,再写一个显示聊天信息的方法就可以了,显示聊天信息的方法不再解释,如下:

//显示聊天信息
function showChat(message) {
    var response = document.getElementById(‘chat_content‘);
    response.value += decodeURIComponent(message.name) + ‘:‘ + decodeURIComponent(message.chatContent) + ‘\n‘;
}

 因为之前处理中文问题,所以发到后台的数据是转码了的,从后台发回来之后,也需要将编码转回来。

到这里,聊天功能就已经做完了,运行程序,会发现,真的可以聊天了!一个聊天程序,就是这么简单。
但是这样并不能满足,往后的功能可以发挥我们的想象力来添加,比如说:我觉得,聊天程序,至少也要缓存一些聊天记录,不然后进来的用户都不知道之前的用户在聊什么,用户体验会非常不好,接下来就看看聊天记录的缓存是怎么实现的吧。
 
转载:http://blog.csdn.net/xjyzxx/article/details/38542665
github地址:https://github.com/xjyaikj/OnlinePreparation

(转)Spring WebSocket教程

标签:

原文地址:http://www.cnblogs.com/liangjiahao/p/4303281.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!