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協定來播送新聞到一切感興致的客戶端或指定用戶。