springMvc+spring 整合 websocket

    根据项目需求,要实现消息提醒功能,首先想到的则是websocket。浏览学习网上现在博客论坛。各种写法的都有,结合自己的项目框架,以及遇到的问题,在这写一写,供大家参考。至于websocket 是啥,原理啥的,该不多说,网上一搜一大把。直接进入主题。

    一、首先介绍一下我们的框架

      jdk1.8 +  springMVC 4.3.8 + mybatisPlus 2.1.9 + tomcat7 + easyui

    二、下面开始进入正题。

    1、首先在maven的pom.xml中引入websocket依赖的jar包。(注意:spring-websocket的版本好最好跟框架的spring版本一致

                <!-- websocket jar -->
<!-- https://mvnrepository.com/artifact/javax.websocket/javax.websocket-api -->
<dependency>
   <groupId>javax.websocket</groupId>
   <artifactId>javax.websocket-api</artifactId>
   <version>1.1</version>
   <scope>provided</scope>
</dependency>


                <!-- https://mvnrepository.com/artifact/org.springframework/spring-websocket -->
<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-websocket</artifactId>
   <version>${spring.version}</version>

</dependency>

    2、更新web.xml中namespace.xsd的版本。(即把相应代码复制进去即可,如果有springmvc的配置文件,最好也更新一下)

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd"

id="WebApp_ID" version="3.0">

3、新建 HandshakeInterceptor.java ,这个类是握手类,作用就是在连接成功前和成功后增加一些额外的功能,一般是把当前握手的用户的唯一标识记录一下,Global.java类是一个工具类,两个常量(注意:一共需要三个类,最好统一放在一个文件夹下面)


public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) throws Exception {
System.out.println("Before Handshake--websocket 握手之前的方法");

// 解决The extension [x-webkit-deflate-frame] is not supported问题
        if (request.getHeaders().containsKey("Sec-WebSocket-Extensions")) {
            request.getHeaders().set("Sec-WebSocket-Extensions",
                    "permessage-deflate");
        }
        
//使用userid区分WebSocketHandler,以便定向发送消息(使用shiro获取session)  
        String userid = Global.userId;  
        if (userid == null) {  
        userid = "default-system";  
        }  
        attributes.put(Global.WEBSOCKET_USERID, userid);  
        
return super.beforeHandshake(request, response, wsHandler, attributes);
}


@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Exception ex) {
System.out.println("After Handshake--websocket 握手之后的方法");
super.afterHandshake(request, response, wsHandler, ex);
}
}

4、新建 WebsocketBusiness.java , webcocket 处理类,这个类是对消息的一些处理,比如是发给一个人,还是发给所有人,并且前端连接时触发的一些动作。(该类里面存在关于我的系统的业务模块,偷懒没有删除,可自行删除哦)


public class WebsocketBusiness extends TextWebSocketHandler {


/**
* 通知公告逻辑层
*/
@Autowired
private ISysUserreadnoticeService iSysUserreadnoticeService;
/**

*/
@Autowired
private ISysUserreadmsgService sysUserreadmsgService;


private static final Map<String, WebSocketSession> users; // 这个会出现性能问题,最好用Map来存储,key用userid
static {
users = new HashMap<>();
}


/**
* 获取当前在线用户

* @return
*/
public static Map<String, WebSocketSession> getUsers() {
return users;
}


/**
* 获取当前在线人数数量

* @return
*/
public static int getUserCount() {
return users.size();
}


/**
* 连接成功时候,会触发UI上onopen方法
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
System.out.println("成功链接 websocket......");


String userid = (String) session.getAttributes().get(Global.WEBSOCKET_USERID);
users.put(userid, session);


// 这块会实现自己业务,比如,当用户登录后,会把离线消息推送给用户
// 用户登录成功后,查询是否存在未读的公告内容,如果存在,则提醒用户有未读公告
EntityWrapper<SysUserreadnotice> suEW = new EntityWrapper<>();
suEW.eq("USERID", userid);
suEW.eq("HASREAD", 0);
suEW.eq("DELFLAG", 0);
int noReadCount = iSysUserreadnoticeService.selectCount(suEW);

// 用户登录成功后,查询是否存在未读的邮件,如果存在,则提醒用户有未读邮件
EntityWrapper<SysUserreadmsg> sumEW = new EntityWrapper<>();
sumEW.eq("USERID", userid);
sumEW.eq("HASREAD", 0);
sumEW.eq("DELFLAG", 0);
sumEW.eq("MSGFOLDER", "收件箱");

int noReadMsgCount = sysUserreadmsgService.selectCount(sumEW);


// 存在未读的则发送消息
if (noReadCount > 0 || noReadMsgCount > 0) {
JSONObject json = new JSONObject();
json.put("wdCount", noReadCount + noReadMsgCount);
json.put("content", "<a href='javascript:socketToMyNotice();'>您有" + noReadCount + "条未读通知公告哦!</a>");

TextMessage returnMessage = new TextMessage(json.toString());

session.sendMessage(returnMessage);
}
}


/**
* 在UI在用js调用websocket.send()时候,会调用该方法
*/
@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);


sendMessageToUsers(message);
}


/**
* 给某个用户发送消息

* @param userid
* @param message
*/
public static void sendMessageToUser(String userid, TextMessage message) {
for (Map.Entry<String, WebSocketSession> entry : users.entrySet()) {
if (userid.equals(entry.getKey())) {
WebSocketSession user = entry.getValue();
try {
if (user.isOpen()) {
user.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}


/**
* 给所有在线用户发送消息

* @param message
*/
public static void sendMessageToUsers(TextMessage message) {
WebSocketSession user = null;
for (Map.Entry<String, WebSocketSession> entry : users.entrySet()) {
user = entry.getValue();
try {
if (user.isOpen()) {
user.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}


@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
if (session.isOpen()) {
session.close();
}
String userid = (String) session.getAttributes().get(Global.WEBSOCKET_USERID);
users.remove(userid);
}


@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
String userid = (String) session.getAttributes().get(Global.WEBSOCKET_USERID);
users.remove(userid);
}


@Override
public boolean supportsPartialMessages() {
return false;
}
}



5、新建 WebSocketConfig.java ,  这个类是配置类,所以需要在spring mvc配置文件中加入对这个类的扫描,第一个addHandler是对正常连接的配置,第二个是如果浏览器不支持websocket,使用socketjs模拟websocket的连接。(注意:该类一定要要扫描到才可以哦,即springmvc 的扫描

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;


@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {


@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(websocketBusiness(),"/websocket").addInterceptors(new HandshakeInterceptor());  
        registry.addHandler(websocketBusiness(), "/sockjs/websocket").addInterceptors(new HandshakeInterceptor()).withSockJS(); 
}


@Bean
public WebSocketHandler websocketBusiness() {
return new WebsocketBusiness();
}
}

6、到此,后端的代码模块基本完成,只剩下前端页面创建请求,进行握手连接了。

7、前端JS。sockjs 可使用cdn的,也可以下载下来放入到项目中。

<!-- 以下是WEBSOCKET握手 -->

<!-- <script src="http://cdn.sockjs.org/sockjs-0.3.min.js"></script> -->

<script src="${staticPath }/static/commons/sockjs-0.3.min.js"></script>

<script>
    var websocket;  
    if ('WebSocket' in window) {  
        websocket = new WebSocket("ws://" + document.location.host + basePath + "/websocket");  
    } else if ('MozWebSocket' in window) {  
        websocket = new MozWebSocket("ws://" + document.location.host + basePath + "/websocket");  
    } else {  
        websocket = new SockJS("http://" + document.location.host + basePath + "/sockjs/websocket");  
    }    

  

   //建立链接 

    websocket.onopen = function(evnt) {
    console.log(evnt);

    };  

    // 接收消息

    websocket.onmessage = function(evnt) {
        if(evnt != null && evnt.data != null) {
        var result = eval('(' + evnt.data + ')');
        var wdCount = result.wdCount;
        var content = result.content;
        //更新未读的数量
        UpdateMsg(wdCount);
       
      $.messager.show({
                title:'消息未读提示',
                msg:content,
                timeout:0
            });
        }
    };  
  //果连接失败,发送、接收数据失败或者处理数据出现错误,browser会触发onerror消息;
    websocket.onerror = function(evnt) {
    console.log(evnt);
    websocket = null;
    }; 
    //当Browser接收到WebSocketServer端发送的关闭连接请求时,就会触发onclose消息。
    websocket.onclose = function(evnt) {
    console.log(evnt);
    websocket = null;
    };
    
    
  //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。  
    window.onbeforeunload = function () {  
    websocket.close(); 
    }  
  
</script>

8、到此,整合websocket完成。测试了几次后,正常使用。OK。可以实现消息提醒。

9、遇到的小问题。引入websocket包后,功能全部正常,但是我们系统引用了Junit 做测试,Junit环境异常。如果遇到该问题,则在pom.xml 中再引入相应的包即可。

<!-- 该JAR 解决 使用WebSocket在Junit环境下报 No suitable default RequestUpgradeStrategy -->
<dependency>
  <groupId>org.apache.tomcat.embed</groupId>
  <artifactId>tomcat-embed-websocket</artifactId>
  <version>7.0.86</version>

</dependency>

10、全部结束。该文章是为了自己梳理一下,仅供参考。勿喷哦。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值