当前位置: 移动技术网 > IT编程>开发语言>Java > Spring整合WebSocket应用示例(上)

Spring整合WebSocket应用示例(上)

2019年07月22日  | 移动技术网IT编程  | 我要评论

防臭地漏,韭黄的功效,教师借款25万

以下教程是小编在参与开发公司的一个crm系统,整理些相关资料,在该系统中有很多消息推送功能,在其中用到了websocket技术。下面小编整理分享到移动技术网平台供大家参考

1. maven依赖

<dependency>
<groupid>javax.servlet</groupid>
<artifactid>javax.servlet-api</artifactid>
<version>3.1.0</version>
</dependency>
<dependency>
<groupid>com.fasterxml.jackson.core</groupid>
<artifactid>jackson-core</artifactid>
<version>2.3.0</version>
</dependency>
<dependency>
<groupid>com.fasterxml.jackson.core</groupid>
<artifactid>jackson-databind</artifactid>
<version>2.3.0</version>
</dependency>
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-websocket</artifactid>
<version>4.0.1.release</version>
</dependency>
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-messaging</artifactid>
<version>4.0.1.release</version>
</dependency>

2. spring-servlet的配置

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx" 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-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/websocket
http://www.springframework.org/schema/websocket/spring-websocket.xsd">
......
<!-- websocket -->
<bean id="websocket" class="cn.bridgeli.websocket.websocketendpoint"/>
<websocket:handlers>
<websocket:mapping path="/websocket" handler="websocket"/>
<websocket:handshake-interceptors>
<bean class="cn.bridgeli.websocket.handshakeinterceptor"/>
</websocket:handshake-interceptors>
</websocket:handlers>
</beans>

其中,path对应的路径就是前段通过ws协议调的接口路径

3. handshakeinterceptor的实现

package cn.bridgeli.websocket;
import cn.bridgeli.utils.usermanager;
import cn.bridgeli.util.dateutil;
import cn.bridgeli.sharesession.userinfo;
import org.apache.commons.lang.stringutils;
import org.slf4j.logger;
import org.slf4j.loggerfactory;
import org.springframework.http.server.serverhttprequest;
import org.springframework.http.server.serverhttpresponse;
import org.springframework.web.context.request.requestcontextholder;
import org.springframework.web.context.request.servletrequestattributes;
import org.springframework.web.socket.websockethandler;
import org.springframework.web.socket.server.support.httpsessionhandshakeinterceptor;
import java.util.date;
import java.util.map;
/**
* @description :创建握手(handshake)接口
* @date : 16-3-3
*/
public class handshakeinterceptor extends httpsessionhandshakeinterceptor{
private static final logger logger = loggerfactory.getlogger(handshakeinterceptor.class);
@override
public boolean beforehandshake(serverhttprequest request,
serverhttpresponse response, websockethandler wshandler,
map<string, object> attributes) throws exception {
logger.info("建立握手前...");
servletrequestattributes attrs = (servletrequestattributes) requestcontextholder.getrequestattributes();
userinfo curruser = usermanager.getsessionuser(attrs.getrequest());
usersocketvo usersocketvo = new usersocketvo();
string email= "";
if(null != curruser){
email = curruser.getemail();
}
if(stringutils.isblank(email)){
email = dateutil.date2string(new date());
}
usersocketvo.setuseremail(email);
attributes.put("session_user", usersocketvo);
return super.beforehandshake(request, response, wshandler, attributes);
}
@override
public void afterhandshake(serverhttprequest request,
serverhttpresponse response, websockethandler wshandler,
exception ex) {
logger.info("建立握手后...");
super.afterhandshake(request, response, wshandler, ex);
}
}

因为老夫不是很懂,所以最大限度的保留原代码,这其实就是从单点登录中取出当前登录用户,转成usersocketvo对象,放到map中。所以接下来我们看看usersocketvo对象的定义

4. usersocketvo的定义

package cn.bridgeli.websocket;
import org.springframework.web.socket.websocketsession;
import java.util.date;
/**
* @description : 用户socket连接实体
* @date : 16-3-7
*/
public class usersocketvo {
private string useremail; //用户邮箱
private date connectiontime; //成功连接时间
private date prerequesttime; //上次请求时间
private date newrequesttime; //新请求时间
private date lastsendtime = new date(); //下架消息最近一次发送时间
private date lasttasksendtime = new date(); //待处理任务最近一次发送时间
private websocketsession websocketsession; //用户对应的wssession 默认仅缓存一个
// getxx and setxx 
}

其中最重要的就是这个websocketsession这个属性了,后面我们要用到

5. websocketendpoint的实现

package cn.bridgeli.websocket;
import org.slf4j.logger;
import org.slf4j.loggerfactory;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.web.socket.closestatus;
import org.springframework.web.socket.textmessage;
import org.springframework.web.socket.websocketsession;
import org.springframework.web.socket.handler.textwebsockethandler;
/**
* @description : websocket处理类
* @date : 16-3-3
*/
public class websocketendpoint extends textwebsockethandler{
private static final logger logger = loggerfactory.getlogger(websocketendpoint.class);
@autowired
private newslistenerimpl newslistener;
@override
protected void handletextmessage(websocketsession session,
textmessage message) throws exception {
super.handletextmessage(session, message);
textmessage returnmessage = new textmessage(message.getpayload()+" received at server");
session.sendmessage(returnmessage);
}
/**
* @description : 建立连接后
* @param session
* @throws exception
*/
@override
public void afterconnectionestablished(websocketsession session) throws exception{
usersocketvo usersocketvo = (usersocketvo)session.getattributes().get("session_user");
if(null != usersocketvo){
usersocketvo.setwebsocketsession(session);
if(wssessionlocalcache.exists(usersocketvo.getuseremail())){
wssessionlocalcache.remove(usersocketvo.getuseremail());
}
wssessionlocalcache.put(usersocketvo.getuseremail(), usersocketvo);
newslistener.afterconnectionestablished(usersocketvo.getuseremail());
}
logger.info("socket成功建立连接...");
super.afterconnectionestablished(session);
}
@override
public void afterconnectionclosed(websocketsession session,closestatus status) throws exception{
usersocketvo usersocketvo = (usersocketvo)session.getattributes().get("session_user");
if(null != usersocketvo){
wssessionlocalcache.remove(usersocketvo.getuseremail());
}
logger.info("socket成功关闭连接...");
super.afterconnectionclosed(session, status);
}
}

6. wssessionlocalcache的实现

package cn.bridgeli.websocket;
import java.io.serializable;
import java.util.arraylist;
import java.util.hashmap;
import java.util.list;
import java.util.map;
/**
* @description :本地缓存websocketsession实例
* @date : 16-3-7
*/
public class wssessionlocalcache implements serializable {
private static map<string, usersocketvo> wssessioncache = new hashmap<>();
public static boolean exists(string useremail){
if(!wssessioncache.containskey(useremail)){
return false;
}else{
return true;
}
}
public static void put(string useremail, usersocketvo usersocketvo){
wssessioncache.put(useremail, usersocketvo);
}
public static usersocketvo get(string useremail){
return wssessioncache.get(useremail);
}
public static void remove(string useremail){
wssessioncache.remove(useremail);
}
public static list<usersocketvo> getallsessions(){
return new arraylist<>(wssessioncache.values());
}
}

看了其实现,作用就比较明显了吧,存放每个usersocketvo的最新数据,其实到这里我们websocket的实现已经算完了,但还有一个核心类(关于业务逻辑查理的类)没有实现,下篇spring整合websocket应用示例(下),我们就看怎么实现这个类。

websocket协议介绍

websocket协议是rfc-6455规范定义的一个web领域的重要的功能:全双工,即客户端和服务器之间的双向通信。它是一个令人兴奋的功能,业界在此领域上已经探索很久,使用的技术包括java applet、xmlhttprequest、adobe flash、activexobject、各种comet技术、服务器端的发送事件等。

需要理解一点,在使用websocket协议前,需要先使用http协议用于构建最初的握手。这依赖于一个机制——建立http,请求协议升级(或叫协议转换)。当服务器同意后,它会响应http状态码101,表示同意切换协议。假设通过tcp套接字成功握手,http协议升级请求通过,那么客户端和服务器端都可以彼此互发消息。

spring框架4.0以上版本引入了一个新模块,即spring-websocket模块。它对websocket通信提供了支持。它兼容java websocket api规范jsr-356,同时提供了额外的功能。

什么场景下该使用websocket

在web应用中,客户端和服务器端需要以较高频率和较低延迟来交换事件时,适合用websocket。因此websocket适合财经、游戏、协作等应用场景。
对于其他应用场景则未必适合。例如,某个新闻订阅需要显示突发新闻,使用间隔几分钟的长轮询也是可以的,这里的延迟可以接受。
即使在要求低延迟的应用场景,如果传输的消息数很低(比如监测网络故障的场景),那么应该考虑使用长轮询技术。

而只有在低延迟和高频消息通信的场景下,选用websocket协议才是非常适合的。即使是这样的应用场景,仍然存在是选择websocket通信呢?又或者是选择rest http通信呢?
答案是会根据应用程序的需求而定。但是,也可能同时使用这两种技术,把需要频繁交换的数据放到websocket中实现,而把rest api作为过程性的业务的实现技术。另外,当rest api的调用中需要把某个信息广播给多个客户端是,也可以通过websocket连接来实现。

spring框架提供了@controller注释和@restcontroller注释,两者都可以用于http请求的处理以及websocket消息的处理。另外,spring mvc的请求处理方法,或其它应用程序的请求处理方法,都可以很容易地使用websocket协议来广播消息到所有感兴趣的客户端或指定用户。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网