Spring整合WebSocket運用示例(上)。本站提示廣大學習愛好者:(Spring整合WebSocket運用示例(上))文章只能為提供參考,不一定能成為您想要的結果。以下是Spring整合WebSocket運用示例(上)正文
以下教程是小編在介入開辟公司的一個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協定來播送新聞到一切感興致的客戶端或指定用戶。